import { getDb } from "./db";
import type { ImportFormatRow, ImportSource } from "../shared/types";

/**
 * What a writer hands to this service: the eight format fields in their
 * persisted shape (produced by `formatToRow` — never assembled by hand) plus
 * the source's own columns.
 *
 * Note it is NOT `Omit<ImportSource, "id" | ...>`: `ImportSource.has_header`
 * is declared boolean while the codec emits the 0/1 SQLite actually stores.
 * Taking `ImportFormatRow` here is what makes the codec the only place that
 * normalization happens.
 */
export interface ImportSourceInput extends ImportFormatRow {
  name: string;
  description?: string | null;
  /** Drift-detection metadata, written by #330. */
  header_signature?: string | null;
  /** Provenance tag — recorded and displayed, never re-read as format. */
  template_id?: number | null;
}

export async function getAllSources(): Promise<ImportSource[]> {
  const db = await getDb();
  return db.select<ImportSource[]>("SELECT * FROM import_sources ORDER BY name");
}

export async function getSourceByName(
  name: string
): Promise<ImportSource | null> {
  const db = await getDb();
  const rows = await db.select<ImportSource[]>(
    "SELECT * FROM import_sources WHERE name = $1",
    [name]
  );
  return rows.length > 0 ? rows[0] : null;
}

export async function getSourceById(
  id: number
): Promise<ImportSource | null> {
  const db = await getDb();
  const rows = await db.select<ImportSource[]>(
    "SELECT * FROM import_sources WHERE id = $1",
    [id]
  );
  return rows.length > 0 ? rows[0] : null;
}

export async function createSource(
  source: ImportSourceInput
): Promise<number> {
  const db = await getDb();
  const result = await db.execute(
    `INSERT INTO import_sources (name, description, date_format, delimiter, encoding, column_mapping, skip_lines, has_header, amount_mode, sign_convention, template_id)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
     ON CONFLICT(name) DO UPDATE SET
       description = excluded.description,
       date_format = excluded.date_format,
       delimiter = excluded.delimiter,
       encoding = excluded.encoding,
       column_mapping = excluded.column_mapping,
       skip_lines = excluded.skip_lines,
       has_header = excluded.has_header,
       amount_mode = excluded.amount_mode,
       sign_convention = excluded.sign_convention,
       template_id = excluded.template_id,
       updated_at = CURRENT_TIMESTAMP`,
    [
      source.name,
      source.description ?? null,
      source.date_format,
      source.delimiter,
      source.encoding,
      source.column_mapping,
      source.skip_lines,
      source.has_header,
      source.amount_mode,
      source.sign_convention,
      source.template_id ?? null,
    ]
  );
  // On conflict, lastInsertId may be 0 — look up the existing row
  if (result.lastInsertId) return result.lastInsertId as number;
  const existing = await getSourceByName(source.name);
  return existing!.id;
}

export async function updateSource(
  id: number,
  source: Partial<ImportSourceInput>
): Promise<void> {
  const db = await getDb();
  const fields: string[] = [];
  const values: unknown[] = [];
  let paramIndex = 1;

  const setColumn = (column: string, value: unknown) => {
    fields.push(`${column} = $${paramIndex++}`);
    values.push(value);
  };

  if (source.name !== undefined) setColumn("name", source.name);
  if (source.description !== undefined) setColumn("description", source.description);
  if (source.date_format !== undefined) setColumn("date_format", source.date_format);
  if (source.delimiter !== undefined) setColumn("delimiter", source.delimiter);
  if (source.encoding !== undefined) setColumn("encoding", source.encoding);
  if (source.column_mapping !== undefined) setColumn("column_mapping", source.column_mapping);
  if (source.skip_lines !== undefined) setColumn("skip_lines", source.skip_lines);
  if (source.has_header !== undefined) setColumn("has_header", source.has_header);
  if (source.amount_mode !== undefined) setColumn("amount_mode", source.amount_mode);
  if (source.sign_convention !== undefined) setColumn("sign_convention", source.sign_convention);
  if (source.header_signature !== undefined) setColumn("header_signature", source.header_signature);
  if (source.template_id !== undefined) setColumn("template_id", source.template_id);

  if (fields.length === 0) return;

  fields.push(`updated_at = CURRENT_TIMESTAMP`);
  values.push(id);

  await db.execute(
    `UPDATE import_sources SET ${fields.join(", ")} WHERE id = $${paramIndex}`,
    values
  );
}

export async function deleteSource(id: number): Promise<void> {
  const db = await getDb();
  await db.execute("DELETE FROM import_sources WHERE id = $1", [id]);
}
