/**
 * @copyright Sister Software
 * @license AGPL-3.0
 * @author Teffen Ellis, et al.
 */
import sqlite from "better-sqlite3";
import { Alpha3bToAlpha2, isAlpha3bLanguageCode, } from "mailwoman/core/resources/languages";
import { PathBuilder } from "path-ts";
import { tryWithBackoff } from "./DataSourceCache.js";
/**
 * A data source for WhosOnFirst placetype records.
 */
export class PlacetypeDataSource {
    #db;
    static createPath({ placetype, languageCode, dataDirectory, }) {
        const normalizedLanguageCode = isAlpha3bLanguageCode(languageCode)
            ? Alpha3bToAlpha2.get(languageCode)
            : languageCode;
        return PathBuilder.from(dataDirectory, placetype, `${normalizedLanguageCode}.db`);
    }
    prepareTables() {
        this.#db.exec(/* sql */ `

			CREATE TABLE IF NOT EXISTS records (
				'id' INTEGER NOT NULL,
				'src' TEXT NOT NULL,
				'name' TEXT NOT NULL,
				'preferred' TEXT,
				'variant' TEXT,
				'colloquial' TEXT,
				'abbr' TEXT,
				'short' TEXT,
				'parent_id' INTEGER,
				PRIMARY KEY ('id', 'src', 'name')
			);
		`);
    }
    prepareIndexes() {
        this.#db.exec(/* sql */ `

			CREATE INDEX IF NOT EXISTS idx_id ON records ('id');
			CREATE INDEX IF NOT EXISTS idx_src ON records ('src');
			CREATE INDEX IF NOT EXISTS idx_name ON records ('name');
			CREATE INDEX IF NOT EXISTS idx_preferred ON records ('preferred');
			CREATE INDEX IF NOT EXISTS idx_variant ON records ('variant');
			CREATE INDEX IF NOT EXISTS idx_colloquial ON records ('colloquial');
			CREATE INDEX IF NOT EXISTS idx_abbr ON records ('abbr');
			CREATE INDEX IF NOT EXISTS idx_short ON records ('short');
			CREATE INDEX IF NOT EXISTS idx_parent_id ON records ('parent_id');
		`);
    }
    constructor(databasePath, dbOptions) {
        this.#db = sqlite(databasePath.toString(), dbOptions);
        this.#db.pragma("busy_timeout = 10000");
        this.#db.pragma("journal_mode = WAL");
        this.#db.pragma("synchronous = OFF");
        this.prepareTables();
        //this.prepareIndexes()
    }
    [Symbol.dispose]() {
        this.#db.close();
    }
    /**
     * Find a placetype record by at least one criteria.
     */
    find(criteria) {
        const statement = this.#db.prepare(/* sql */ `
			SELECT *
			FROM records
			WHERE ${Object.keys(criteria)
            .map((key) => `${key} = @${key}`)
            .join(" OR ")}
		`);
        return Iterator.from(statement.iterate());
    }
    /**
     * Given a placetype record, insert or update into the database.
     *
     * @param record The placetype record to upsert.
     */
    async upsert(record) {
        const perform = () => {
            const statement = this.#db.prepare(/* sql */ `
			INSERT INTO records
			(id, src, name, preferred, variant, colloquial, abbr, short, parent_id)
			VALUES (
				@id,
				@src,
				@name,
				@preferred,
				@variant,
				@colloquial,
				@abbr,
				@short,
				@parent_id
			)
			ON CONFLICT (id, src, name)
			DO UPDATE SET
			preferred = excluded.preferred,
			variant = excluded.variant,
			colloquial = excluded.colloquial,
			abbr = excluded.abbr,
			short = excluded.short,
			parent_id = excluded.parent_id
			`);
            statement.run(record);
        };
        await tryWithBackoff(5, perform);
    }
}
//# sourceMappingURL=PlacetypeDataSource.js.map