// {{generatedComment}}
/* eslint-disable */
// biome-ignore-all lint: generated code
import type { Database, RunResult, Statement } from 'better-sqlite3';

export class {{className}} {
    private statements = new Map<string, Statement>();

    constructor(private db: Database) {}

    private prepare<BindParameters extends unknown[] | {} = unknown[], Result = unknown>(id: string, query: string, isPluck = false) {
        let stmt = this.statements.get(id);
        if (!stmt) {
            stmt = this.db.prepare(query);
            if (isPluck) {
                stmt = stmt.pluck();
            }
            this.statements.set(id, stmt);
        }
        return stmt as Statement<BindParameters, Result>;
    }

    static getMigrations(): string[] {
        return [
            {{#each migrations}}
            {{{quote sqlQuery}}},
            {{/each}}
        ];
    }

{{#if config.migrations}}
    static applyMigrations(db: Database, projectName = '{{projectName}}'): void {
        db.exec(`CREATE TABLE IF NOT EXISTS _sqg_migrations (
            project TEXT NOT NULL,
            migration_id TEXT NOT NULL,
            applied_at TEXT NOT NULL DEFAULT (datetime('now')),
            PRIMARY KEY (project, migration_id)
        )`);
        const runMigrations = db.transaction(() => {
            const applied = new Set(
                db.prepare('SELECT migration_id FROM _sqg_migrations WHERE project = ?')
                    .pluck().all(projectName) as string[]
            );
            const migrations: [string, string][] = [
                {{#each migrations}}
                ['{{{id}}}', {{{quote sqlQuery}}}],
                {{/each}}
            ];
            for (const [id, sql] of migrations) {
                if (!applied.has(id)) {
                    db.exec(sql);
                    db.prepare('INSERT INTO _sqg_migrations (project, migration_id) VALUES (?, ?)')
                        .run(projectName, id);
                }
            }
        });
        runMigrations.immediate();
    }
{{/if}}

    static getQueryNames(): Map<string, keyof {{className}}> {
        return new Map([
            {{#each queries}} {{#unless skipGenerateFunction}}
            ["{{id}}", "{{functionName}}"],{{/unless}}{{/each}}]
        );
    }
    
    {{#each queries}}
    {{#unless skipGenerateFunction}}
    {{functionName}}({{#each variables}}{{name}}: {{type}}{{#unless @last}}, {{/unless}}{{/each}}): {{> returnType }} {
        const stmt = this.prepare<[{{> paramTypes}}], {{> resultType}}>('{{id}}', 
            {{{sqlExpr this}}}{{#if isPluck}}, true{{/if}});
        {{> execute}}
    }
    {{/unless}}
    {{/each}}
}

{{#*inline "params"}}{{#each parameterNames}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}{{/inline}}

{{#*inline "paramTypes"}}{{#each parameters}}{{type}} {{#unless @last}}, {{/unless}}{{/each}}{{/inline}}

{{#*inline "columnTypes"}}{{#each columns}}{{name}}: {{mapType .}}{{#unless @last}}, {{/unless}}{{/each}}{{/inline}}

{{#*inline "rowType"}}
{{#if isQuery~}}
{{#if isPluck~}}
{{#if columns.length~}} {{mapType (lookup columns 0)}} {{else}}any{{/if~}}
{{~else~}}
{{#if columns.length}}{ {{> columnTypes}} }{{else}}any{{/if~}}
{{/if~}}
{{~else~}}
any
{{~/if~}}
{{/inline~}}

{{#*inline "resultType"}}
{{#if isQuery~}}
{{> rowType}}
{{~else~}}
any
{{~/if~}}
{{/inline~}}


{{#*inline "returnType"}}
{{#if isQuery~}}
{{#if isOne~}}
{{> rowType}} | undefined
{{~else~}}
({{> rowType}})[]
{{/if~}}
{{~else~}}
RunResult
{{~/if~}}
{{/inline~}}

{{#*inline "execute"}}
{{#if isQuery}}
{{#if isOne}}
return stmt.get({{> params}});
{{else}}
return stmt.all({{> params}});
{{/if}}
{{else}}
return stmt.run({{> params}});
{{/if}}
{{/inline}}