// {{generatedComment}}
/* eslint-disable */
// biome-ignore-all lint: generated code
import { DatabaseSync, StatementSync } from 'node:sqlite';

interface RunResult {
    changes: number;
    lastInsertRowid: number;
}

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

    constructor(private db: DatabaseSync) {}

    private prepare(id: string, query: string) {
        let stmt = this.statements.get(id);
        if (!stmt) {
            stmt = this.db.prepare(query);
            this.statements.set(id, stmt);
        }
        return stmt;
    }

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

{{#if config.migrations}}
    static applyMigrations(db: DatabaseSync, 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)
        )`);
        db.exec('BEGIN IMMEDIATE');
        try {
            const rows = db.prepare('SELECT migration_id FROM _sqg_migrations WHERE project = ?')
                .all(projectName) as { migration_id: string }[];
            const applied = new Set(rows.map(r => r.migration_id));
            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);
                }
            }
            db.exec('COMMIT');
        } catch (e) {
            db.exec('ROLLBACK');
            throw e;
        }
    }
{{/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('{{id}}',
            {{{quote sqlQuery}}});
        {{> 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}}
{{#if isPluck}}
const row = stmt.get({{> params}}) as Record<string, {{> rowType}}> | undefined;
return row ? Object.values(row)[0] : undefined;
{{else}}
return stmt.get({{> params}}) as {{> rowType}} | undefined;
{{/if}}
{{else}}
{{#if isPluck}}
const rows = stmt.all({{> params}}) as Record<string, {{> rowType}}>[];
return rows.map(row => Object.values(row)[0]);
{{else}}
return stmt.all({{> params}}) as ({{> rowType}})[];
{{/if}}
{{/if}}
{{else}}
return stmt.run({{> params}}) as RunResult;
{{/if}}
{{/inline}}
