import { Pool, PoolConfig, QueryResult, QueryResultRow } from "pg";
import { config } from "./config";
import type { RaceRow } from "./mappers/race";

const poolConfig: PoolConfig = {
  host: config.db.host,
  port: config.db.port,
  database: config.db.database,
  user: config.db.user,
  password: config.db.password,
  max: 10,
  idleTimeoutMillis: 30_000,
  connectionTimeoutMillis: 5_000,
};

function mockRowFromInsert(sql: string, params: unknown[]): RaceRow {
  const match = sql.match(/INSERT INTO races\s*\(([^)]+)\)\s*VALUES/i);
  const now = new Date();
  if (!match) {
    return {
      id: String(params[0] ?? ""),
      race_date: "",
      title: "",
      distance_km: "0",
      status: null,
      official_url: null,
      start_time: null,
      cluster_schedule: null,
      bib_pickup: null,
      bib_number: null,
      finish_time: null,
      finish_place: null,
      notes: null,
      created_at: now,
      updated_at: null,
    };
  }
  const cols = match[1].split(",").map((c) => c.trim());
  const row: Record<string, unknown> = {};
  cols.forEach((col, i) => {
    row[col] = params[i];
  });
  return {
    id: String(row.id ?? ""),
    race_date: String(row.race_date ?? ""),
    title: String(row.title ?? ""),
    distance_km: String(row.distance_km ?? "0"),
    status: row.status != null ? String(row.status) : null,
    official_url: row.official_url != null ? String(row.official_url) : null,
    start_time: row.start_time != null ? String(row.start_time) : null,
    cluster_schedule: row.cluster_schedule != null ? String(row.cluster_schedule) : null,
    bib_pickup: row.bib_pickup != null ? String(row.bib_pickup) : null,
    bib_number: row.bib_number != null ? String(row.bib_number) : null,
    finish_time: row.finish_time != null ? String(row.finish_time) : null,
    finish_place: row.finish_place != null ? String(row.finish_place) : null,
    notes: row.notes != null ? String(row.notes) : null,
    created_at: now,
    updated_at: null,
  };
}

function createMockPool(): Pool {
  const emptyResult = <T extends QueryResultRow>(): QueryResult<T> =>
    ({
      rows: [],
      rowCount: 0,
      command: "",
      oid: 0,
      fields: [],
    }) as QueryResult<T>;

  const store = new Map<string, RaceRow>();

  const mockQuery = async <T extends QueryResultRow>(
    text: string,
    params?: unknown[],
  ): Promise<QueryResult<T>> => {
    const sql = text.replace(/\s+/g, " ").trim();
    const p = params ?? [];

    if (sql.includes("INSERT INTO races") && sql.includes("RETURNING")) {
      const row = mockRowFromInsert(text, p);
      store.set(row.id, row);
      return {
        rows: [row as unknown as T],
        rowCount: 1,
        command: "INSERT",
        oid: 0,
        fields: [],
      } as QueryResult<T>;
    }

    if (sql.includes("DELETE FROM races")) {
      const id = String(p[0] ?? "");
      const existed = store.delete(id);
      return {
        rows: [],
        rowCount: existed ? 1 : 0,
        command: "DELETE",
        oid: 0,
        fields: [],
      } as QueryResult<T>;
    }

    if (sql.includes("UPDATE races") && sql.includes("RETURNING")) {
      const id = String(p[p.length - 1] ?? "");
      const existing = store.get(id);
      if (!existing) {
        return emptyResult();
      }
      const updated = { ...existing, updated_at: new Date() };
      store.set(id, updated);
      return {
        rows: [updated as unknown as T],
        rowCount: 1,
        command: "UPDATE",
        oid: 0,
        fields: [],
      } as QueryResult<T>;
    }

    if (sql.includes("SELECT * FROM races WHERE id =")) {
      const id = String(p[0] ?? "");
      const row = store.get(id);
      return row
        ? { rows: [row as unknown as T], rowCount: 1, command: "SELECT", oid: 0, fields: [] } as QueryResult<T>
        : emptyResult();
    }

    if (sql.includes("SELECT * FROM races")) {
      const rows = Array.from(store.values());
      return { rows: rows as unknown as T[], rowCount: rows.length, command: "SELECT", oid: 0, fields: [] } as QueryResult<T>;
    }

    return emptyResult();
  };

  const mockPool = {
    query: mockQuery,
    connect: async () => {
      throw new Error(
        "CALENDAR_RUN_MOCK_DB is enabled: migrate/seed require a real database; unset CALENDAR_RUN_MOCK_DB and configure DB_*.",
      );
    },
    end: async () => {},
    on() {
      return mockPool;
    },
  };

  return mockPool as unknown as Pool;
}

export const pool = config.useMockDb ? createMockPool() : new Pool(poolConfig);

if (!config.useMockDb) {
  pool.on("error", (err) => {
    console.error("[db] Unexpected pool error:", err.message);
  });
} else {
  console.warn("[db] Mock database enabled (CALENDAR_RUN_MOCK_DB); no PostgreSQL connection is used.");
}

export async function checkDbConnection(): Promise<boolean> {
  if (config.useMockDb) {
    return true;
  }
  try {
    const client = await pool.connect();
    client.release();
    return true;
  } catch {
    return false;
  }
}
