import type Database from "better-sqlite3";

export function initializeSchema(db: Database.Database) {
  db.exec(`
    create table if not exists brand_templates (
      id text primary key,
      brand_name text not null,
      company_full_name text not null,
      company_short_names text not null,
      product_names text not null,
      target_industries text not null,
      target_audience text not null,
      verified_claims text not null,
      forbidden_claims text not null,
      tone_rules text not null,
      created_at text not null,
      updated_at text not null
    );

    create table if not exists article_jobs (
      id text primary key,
      brand_template_id text,
      case_id text,
      source_title text not null,
      source_body text not null,
      image_inputs text not null,
      publish_platform text not null,
      user_instructions text not null,
      status text not null,
      export_paths text not null,
      created_at text not null,
      updated_at text not null,
      foreign key (brand_template_id) references brand_templates(id)
    );

    create table if not exists optimization_cases (
      id text primary key,
      case_type text not null,
      title text not null,
      summary text not null,
      status text not null,
      customer_name text not null default '',
      brand_name text not null default '',
      project_tags text not null default '[]',
      notes text not null default '',
      publish_target text not null default '',
      source_excerpt text not null default '',
      result_excerpt text not null default '',
      latest_result_version_id text,
      latest_version_number integer,
      last_error_stage text,
      last_error_summary text,
      archived_at text,
      created_at text not null,
      updated_at text not null
    );

    create table if not exists case_inputs (
      case_id text primary key,
      case_type text not null,
      article_job_id text,
      payload text not null,
      created_at text not null,
      updated_at text not null,
      foreign key (case_id) references optimization_cases(id) on delete cascade,
      foreign key (article_job_id) references article_jobs(id) on delete set null
    );

    create table if not exists fact_cards (
      job_id text primary key,
      source text not null,
      fact_card text not null,
      confirmed_by_user integer not null,
      created_at text not null,
      updated_at text not null,
      foreign key (job_id) references article_jobs(id) on delete cascade
    );

    create table if not exists optimized_articles (
      job_id text not null,
      revision integer not null,
      article text not null,
      created_at text not null,
      primary key (job_id, revision),
      foreign key (job_id) references article_jobs(id) on delete cascade
    );

    create table if not exists qa_reports (
      job_id text not null,
      revision integer not null,
      report text not null,
      created_at text not null,
      primary key (job_id, revision),
      foreign key (job_id, revision)
        references optimized_articles(job_id, revision) on delete cascade
    );

    create table if not exists optimization_result_versions (
      id text primary key,
      case_id text not null,
      case_type text not null,
      version integer not null,
      status text not null,
      article_job_id text,
      article_revision integer,
      result_summary text not null,
      payload text not null,
      process_summary text not null,
      llm_audit_summary text not null,
      error_stage text,
      error_summary text,
      created_at text not null,
      unique (case_id, version),
      foreign key (case_id) references optimization_cases(id) on delete cascade,
      foreign key (article_job_id) references article_jobs(id) on delete set null
    );

    create table if not exists rubric_versions (
      id text primary key,
      version text not null,
      name text not null,
      dimensions text not null,
      formula text not null,
      is_active integer not null,
      created_at text not null
    );

    create table if not exists scoring_runs (
      id text primary key,
      result_version_id text,
      case_type text not null default 'article',
      job_id text,
      revision integer,
      rubric_version_id text not null,
      dimension_scores text not null,
      composite_score real not null,
      rationale text not null,
      created_at text not null,
      foreign key (result_version_id)
        references optimization_result_versions(id) on delete cascade,
      foreign key (rubric_version_id) references rubric_versions(id)
    );

    create table if not exists publication_records (
      id text primary key,
      result_version_id text,
      job_id text,
      revision integer,
      publish_target text not null,
      url text not null,
      published_at text not null,
      status text not null,
      notes text not null,
      created_at text not null,
      updated_at text not null,
      foreign key (result_version_id)
        references optimization_result_versions(id) on delete cascade
    );

    create table if not exists performance_snapshots (
      id text primary key,
      publication_id text not null,
      source text not null,
      window_label text not null,
      metrics text not null,
      feedback_summary text not null,
      raw_reference text,
      snapshot_at text not null,
      foreign key (publication_id) references publication_records(id) on delete cascade
    );

    create table if not exists calibration_events (
      id text primary key,
      publication_id text not null,
      scoring_run_id text not null,
      performance_snapshot_id text not null,
      direction text not null,
      observations text not null,
      recommended_action text not null,
      created_at text not null,
      foreign key (publication_id) references publication_records(id) on delete cascade,
      foreign key (scoring_run_id) references scoring_runs(id) on delete cascade,
      foreign key (performance_snapshot_id)
        references performance_snapshots(id) on delete cascade
    );

    create index if not exists idx_scoring_runs_job_revision
      on scoring_runs(job_id, revision);

    create index if not exists idx_optimization_cases_updated_at
      on optimization_cases(updated_at);

    create index if not exists idx_optimization_cases_case_type
      on optimization_cases(case_type);

    create index if not exists idx_optimization_cases_status
      on optimization_cases(status);

    create index if not exists idx_optimization_result_versions_case
      on optimization_result_versions(case_id, version desc);

    create index if not exists idx_publication_records_job_revision
      on publication_records(job_id, revision);

    create index if not exists idx_performance_snapshots_publication
      on performance_snapshots(publication_id);
  `);

  ensureColumn(db, "article_jobs", "case_id", "text");
  migrateScoringRunsForCases(db);
  ensureColumn(db, "publication_records", "result_version_id", "text");
  ensureColumn(
    db,
    "publication_records",
    "publish_target",
    "text not null default ''",
  );

  const publicationColumns = getTableColumns(db, "publication_records");
  if (publicationColumns.includes("platform")) {
    db.exec(
      "update publication_records set publish_target = platform where publish_target = ''",
    );
  }

  db.exec(`
    create index if not exists idx_scoring_runs_result_version
      on scoring_runs(result_version_id);

    create index if not exists idx_publication_records_result_version
      on publication_records(result_version_id);
  `);
}

function getTableColumns(db: Database.Database, tableName: string) {
  return getTableColumnInfo(db, tableName).map((row) => row.name);
}

function getTableColumnInfo(db: Database.Database, tableName: string) {
  return db
    .prepare(`pragma table_info(${tableName})`)
    .all()
    .map((row) => row as { name: string; notnull: number });
}

function migrateScoringRunsForCases(db: Database.Database) {
  const columns = getTableColumnInfo(db, "scoring_runs");
  const columnNames = columns.map((column) => column.name);
  const jobIdColumn = columns.find((column) => column.name === "job_id");
  const revisionColumn = columns.find((column) => column.name === "revision");
  const needsRebuild =
    !columnNames.includes("result_version_id") ||
    !columnNames.includes("case_type") ||
    jobIdColumn?.notnull === 1 ||
    revisionColumn?.notnull === 1;

  if (!needsRebuild) return;

  const resultVersionSelect = columnNames.includes("result_version_id")
    ? "result_version_id"
    : "NULL";
  const caseTypeSelect = columnNames.includes("case_type")
    ? "case_type"
    : "'article'";

  db.pragma("foreign_keys = OFF");
  try {
    db.exec(`
      drop table if exists scoring_runs_next;

      create table scoring_runs_next (
        id text primary key,
        result_version_id text,
        case_type text not null default 'article',
        job_id text,
        revision integer,
        rubric_version_id text not null,
        dimension_scores text not null,
        composite_score real not null,
        rationale text not null,
        created_at text not null,
        foreign key (result_version_id)
          references optimization_result_versions(id) on delete cascade,
        foreign key (rubric_version_id) references rubric_versions(id)
      );

      insert into scoring_runs_next (
        id, result_version_id, case_type, job_id, revision, rubric_version_id,
        dimension_scores, composite_score, rationale, created_at
      )
      select
        id, ${resultVersionSelect}, ${caseTypeSelect}, job_id, revision,
        rubric_version_id, dimension_scores, composite_score, rationale, created_at
      from scoring_runs;

      drop table scoring_runs;
      alter table scoring_runs_next rename to scoring_runs;
    `);
  } finally {
    db.pragma("foreign_keys = ON");
  }
}

function ensureColumn(
  db: Database.Database,
  tableName: string,
  columnName: string,
  definition: string,
) {
  if (getTableColumns(db, tableName).includes(columnName)) return;
  db.exec(`alter table ${tableName} add column ${columnName} ${definition}`);
}
