import { isRecord } from "../internal/rowMappers.js";
export class RepositoryBindingRepository {
    #db;
    constructor(db) {
        this.#db = db;
    }
    upsertRepositoryBinding(input) {
        const now = new Date().toISOString();
        this.#db
            .prepare(`INSERT INTO repository_bindings
          (id, workspace_id, display_label, provider, status, last_sync_label, last_error_code,
           remote_url, owner, repo, default_branch, local_path, link_state, clone_state,
           last_clone_error_code, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
         ON CONFLICT(id) DO UPDATE SET
           display_label = excluded.display_label,
           provider = excluded.provider,
           status = excluded.status,
           last_sync_label = excluded.last_sync_label,
           last_error_code = excluded.last_error_code,
           remote_url = excluded.remote_url,
           owner = excluded.owner,
           repo = excluded.repo,
           default_branch = excluded.default_branch,
           local_path = excluded.local_path,
           link_state = excluded.link_state,
           clone_state = excluded.clone_state,
           last_clone_error_code = excluded.last_clone_error_code,
           updated_at = excluded.updated_at`)
            .run(input.id, input.workspaceId, input.displayLabel, input.provider, input.status, input.lastSyncLabel ?? null, input.lastErrorCode ?? null, input.remoteUrl ?? null, input.owner ?? null, input.repo ?? null, input.defaultBranch ?? null, input.localPath ?? null, input.linkState ?? "unlinked", input.cloneState ?? "idle", input.lastCloneErrorCode ?? null, now, now);
    }
    /**
     * Transition only the working-copy lifecycle (clone state, local path,
     * derived status, last error) of an existing binding, leaving the remote
     * identity set at link time untouched. A no-op when the id is not present
     * for the workspace.
     */
    updateRepositoryWorkingCopy(input) {
        const now = new Date().toISOString();
        const result = this.#db
            .prepare(`UPDATE repository_bindings
         SET clone_state = ?,
             local_path = COALESCE(?, local_path),
             status = COALESCE(?, status),
             last_clone_error_code = ?,
             updated_at = ?
         WHERE id = ? AND workspace_id = ?`)
            .run(input.cloneState, input.localPath ?? null, input.status ?? null, input.lastCloneErrorCode ?? null, now, input.id, input.workspaceId);
        return changeCount(result) > 0;
    }
    deleteRepositoryBinding(workspaceId, id) {
        const result = this.#db
            .prepare(`DELETE FROM repository_bindings WHERE id = ? AND workspace_id = ?`)
            .run(id, workspaceId);
        return changeCount(result) > 0;
    }
    findRepositoryBinding(workspaceId, id) {
        const row = this.#db
            .prepare(`${SELECT_COLUMNS}
         FROM repository_bindings
         WHERE id = ? AND workspace_id = ?`)
            .get(id, workspaceId);
        if (!isRecord(row))
            return null;
        return mapRepositoryBindingRow(row);
    }
    listRepositoryBindings(workspaceId) {
        const rows = this.#db
            .prepare(`${SELECT_COLUMNS}
         FROM repository_bindings
         WHERE workspace_id = ?
         ORDER BY display_label ASC, id ASC`)
            .all(workspaceId);
        const result = [];
        for (const row of rows) {
            if (!isRecord(row))
                continue;
            result.push(mapRepositoryBindingRow(row));
        }
        return result;
    }
}
const SELECT_COLUMNS = `SELECT id, workspace_id, display_label, provider, status, last_sync_label, last_error_code,
         remote_url, owner, repo, default_branch, local_path, link_state, clone_state,
         last_clone_error_code, created_at, updated_at`;
function mapRepositoryBindingRow(row) {
    const record = {
        id: String(row.id),
        workspaceId: String(row.workspace_id),
        displayLabel: String(row.display_label),
        provider: row.provider,
        status: row.status,
        linkState: normalizeLinkState(row.link_state),
        cloneState: normalizeCloneState(row.clone_state),
        createdAt: String(row.created_at),
        updatedAt: String(row.updated_at),
    };
    if (typeof row.last_sync_label === "string")
        record.lastSyncLabel = row.last_sync_label;
    if (typeof row.last_error_code === "string")
        record.lastErrorCode = row.last_error_code;
    if (typeof row.remote_url === "string")
        record.remoteUrl = row.remote_url;
    if (typeof row.owner === "string")
        record.owner = row.owner;
    if (typeof row.repo === "string")
        record.repo = row.repo;
    if (typeof row.default_branch === "string")
        record.defaultBranch = row.default_branch;
    if (typeof row.local_path === "string")
        record.localPath = row.local_path;
    if (typeof row.last_clone_error_code === "string")
        record.lastCloneErrorCode = row.last_clone_error_code;
    return record;
}
const LINK_STATES = new Set(["unlinked", "linked"]);
const CLONE_STATES = new Set([
    "idle",
    "cloning",
    "cloned",
    "failed",
]);
function normalizeLinkState(value) {
    return typeof value === "string" &&
        LINK_STATES.has(value)
        ? value
        : "unlinked";
}
function normalizeCloneState(value) {
    return typeof value === "string" &&
        CLONE_STATES.has(value)
        ? value
        : "idle";
}
function changeCount(result) {
    if (isRecord(result) && typeof result.changes === "number") {
        return result.changes;
    }
    if (isRecord(result) && typeof result.changes === "bigint") {
        return Number(result.changes);
    }
    return 0;
}
