import { Router, Request, Response } from "express";
import { pool } from "../db";
import { rowToDto, bodyToColumns, RaceRow } from "../mappers/race";

const router = Router();

type ValidationErrorBody = {
  error: "validation_error";
  details: string[];
};

function dbError(res: Response) {
  res.status(503).json({ error: "database_unavailable" });
}

function validationError(res: Response, details: string[]) {
  const body: ValidationErrorBody = { error: "validation_error", details };
  res.status(400).json(body);
}

function parseOptionalIntegerQuery(
  value: unknown,
  fieldName: string,
  min?: number,
  max?: number,
): { value?: number; error?: string } {
  if (value == null) {
    return {};
  }
  if (typeof value !== "string" || value.trim() === "") {
    return { error: `${fieldName} must be an integer` };
  }

  const normalized = value.trim();
  if (!/^-?\d+$/.test(normalized)) {
    return { error: `${fieldName} must be an integer` };
  }

  const parsed = Number(normalized);
  if (!Number.isInteger(parsed)) {
    return { error: `${fieldName} must be an integer` };
  }

  if (min != null && parsed < min) {
    return { error: `${fieldName} must be between ${min} and ${max}` };
  }
  if (max != null && parsed > max) {
    return { error: `${fieldName} must be between ${min} and ${max}` };
  }

  return { value: parsed };
}

/* ─── GET /races ──────────────────────────────────────────── */

router.get("/races", async (req: Request, res: Response) => {
  const yearResult = parseOptionalIntegerQuery(req.query.year, "year");
  const monthResult = parseOptionalIntegerQuery(req.query.month, "month", 1, 12);
  const details = [yearResult.error, monthResult.error].filter(Boolean) as string[];

  if (details.length > 0) {
    validationError(res, details);
    return;
  }

  try {
    const conditions: string[] = [];
    const params: unknown[] = [];
    let idx = 1;

    if (yearResult.value != null) {
      conditions.push(`EXTRACT(YEAR FROM race_date) = $${idx++}`);
      params.push(yearResult.value);
    }
    if (monthResult.value != null) {
      conditions.push(`EXTRACT(MONTH FROM race_date) = $${idx++}`);
      params.push(monthResult.value);
    }

    const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
    const sql = `SELECT * FROM races ${where} ORDER BY race_date ASC`;
    const { rows } = await pool.query<RaceRow>(sql, params);

    res.json(rows.map(rowToDto));
  } catch (err) {
    console.error("[GET /races]", err);
    dbError(res);
  }
});

/* ─── GET /races/:id ──────────────────────────────────────── */

router.get("/races/:id", async (req: Request, res: Response) => {
  try {
    const { rows } = await pool.query<RaceRow>(
      "SELECT * FROM races WHERE id = $1",
      [req.params.id],
    );
    if (rows.length === 0) {
      res.status(404).json({ error: "not_found", details: ["Race not found"] });
      return;
    }
    res.json(rowToDto(rows[0]));
  } catch (err) {
    console.error("[GET /races/:id]", err);
    dbError(res);
  }
});

/* ─── POST /races ─────────────────────────────────────────── */

router.post("/races", async (req: Request, res: Response) => {
  const body = req.body;

  if (!body.id || !body.date || !body.title || body.distanceKm == null) {
    validationError(res, ["Fields id, date, title, distanceKm are required"]);
    return;
  }

  const { columns, values } = bodyToColumns(body);
  columns.unshift("id");
  values.unshift(body.id);

  const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
  const sql = `INSERT INTO races (${columns.join(", ")}) VALUES (${placeholders}) RETURNING *`;

  try {
    const { rows } = await pool.query<RaceRow>(sql, values);
    res.status(201).json(rowToDto(rows[0]));
  } catch (err: any) {
    if (err.code === "23505") {
      res.status(409).json({
        error: "conflict",
        details: ["Race with this id already exists"],
      });
      return;
    }
    console.error("[POST /races]", err);
    dbError(res);
  }
});

/* ─── PATCH /races/:id ────────────────────────────────────── */

router.patch("/races/:id", async (req: Request, res: Response) => {
  const { columns, values } = bodyToColumns(req.body);

  if (columns.length === 0) {
    validationError(res, ["No updatable fields provided"]);
    return;
  }

  const sets = columns.map((col, i) => `${col} = $${i + 1}`);
  sets.push(`updated_at = NOW()`);
  values.push(req.params.id);
  const sql = `UPDATE races SET ${sets.join(", ")} WHERE id = $${values.length} RETURNING *`;

  try {
    const { rows } = await pool.query<RaceRow>(sql, values);
    if (rows.length === 0) {
      res.status(404).json({ error: "not_found", details: ["Race not found"] });
      return;
    }
    res.json(rowToDto(rows[0]));
  } catch (err) {
    console.error("[PATCH /races/:id]", err);
    dbError(res);
  }
});

/* ─── DELETE /races/:id ───────────────────────────────────── */

router.delete("/races/:id", async (req: Request, res: Response) => {
  try {
    const { rowCount } = await pool.query(
      "DELETE FROM races WHERE id = $1",
      [req.params.id],
    );
    if (rowCount === 0) {
      res.status(404).json({ error: "not_found", details: ["Race not found"] });
      return;
    }
    res.status(204).end();
  } catch (err) {
    console.error("[DELETE /races/:id]", err);
    dbError(res);
  }
});

export default router;
