import { pool } from "../config/db.js";
import { ApiError } from "../utils/ApiError.js";
import { getJwtUserId } from "../middlewares/authMiddleware.js";
import { snapshotFormLog } from "../utils/auditLogs.js";
import { archivePtwFormParent } from "../utils/ptwVersioning.js";

const FORM_COLUMNS = [
  "form_parent_id",
  "form_type_id",
  "job_type_id",
  "title",
  "description",
  "created_by",
  "created_date"
];

function toPositiveInt(value, fieldName) {
  const n = Number(value);
  if (!Number.isInteger(n) || n <= 0) {
    throw new ApiError(400, `${fieldName} must be a positive integer`);
  }
  return n;
}

function actorId(req) {
  const raw = getJwtUserId(req.user);
  const n = raw != null ? Number(raw) : NaN;
  return Number.isInteger(n) && n > 0 ? n : null;
}

function pickColumns(body, allowed) {
  const out = {};
  for (const col of allowed) {
    if (Object.prototype.hasOwnProperty.call(body, col) && body[col] !== undefined) {
      out[col] = body[col];
    }
  }
  return out;
}

/** Optional location link(s) on form create: `location_ids: []` or `location_id`. */
function extractLocationIds(body) {
  const ids = [];
  if (body?.location_ids != null) {
    const raw = Array.isArray(body.location_ids) ? body.location_ids : [body.location_ids];
    for (const x of raw) {
      const n = Number(x);
      if (Number.isInteger(n) && n > 0) ids.push(n);
    }
  }
  if (body?.location_id != null && body.location_id !== "") {
    const n = Number(body.location_id);
    if (Number.isInteger(n) && n > 0) ids.push(n);
  }
  return [...new Set(ids)];
}

export async function createForm(req, res, next) {
  const conn = await pool.getConnection();
  try {
    const body = req.body || {};
    const write = pickColumns(body, FORM_COLUMNS);
    const locationIds = extractLocationIds(body);
    const columns = Object.keys(write);
    if (!columns.length) throw new ApiError(400, "Body cannot be empty");

    await conn.beginTransaction();

    const placeholders = columns.map(() => "?").join(",");
    const [result] = await conn.query(
      `INSERT INTO ptw_form_details (${columns.map((c) => `\`${c}\``).join(",")}) VALUES (${placeholders})`,
      columns.map((c) => write[c])
    );
    const formId = Number(result.insertId);

    for (const lid of locationIds) {
      await conn.query(
        "INSERT INTO ptw_location_forms_details (form_id, location_id) VALUES (?, ?)",
        [formId, lid]
      );
    }

    // Creating a child version archives the superseded parent.
    if (write.form_parent_id != null && write.form_parent_id !== "") {
      await archivePtwFormParent(conn, write.form_parent_id);
    }

    await conn.commit();
    res.status(201).json({ insertedId: formId, affectedRows: result.affectedRows });
  } catch (err) {
    await conn.rollback().catch(() => {});
    next(err);
  } finally {
    conn.release();
  }
}

export async function updateForm(req, res, next) {
  const conn = await pool.getConnection();
  try {
    const formId = toPositiveInt(req.params.form_id, "form_id");
    const body = req.body || {};
    const write = pickColumns(body, FORM_COLUMNS);
    const locationIds = body.location_ids != null || body.location_id != null
      ? extractLocationIds(body)
      : null;
    const columns = Object.keys(write);
    if (!columns.length && locationIds == null) {
      throw new ApiError(400, "Body cannot be empty");
    }

    await conn.beginTransaction();
    await snapshotFormLog(conn, formId, actorId(req), "UPDATE");

    let affectedRows = 0;
    if (columns.length) {
      const setClause = columns.map((c) => `\`${c}\` = ?`).join(", ");
      const [result] = await conn.query(
        `UPDATE ptw_form_details SET ${setClause} WHERE form_id = ?`,
        [...columns.map((c) => write[c]), formId]
      );
      affectedRows = result.affectedRows;
    }

    if (locationIds != null) {
      await conn.query("DELETE FROM ptw_location_forms_details WHERE form_id = ?", [formId]);
      for (const lid of locationIds) {
        await conn.query(
          "INSERT INTO ptw_location_forms_details (form_id, location_id) VALUES (?, ?)",
          [formId, lid]
        );
      }
    }

    await conn.commit();
    res.json({ affectedRows });
  } catch (err) {
    await conn.rollback().catch(() => {});
    next(err);
  } finally {
    conn.release();
  }
}

export async function deleteForm(req, res, next) {
  const conn = await pool.getConnection();
  try {
    const formId = toPositiveInt(req.params.form_id, "form_id");

    await conn.beginTransaction();
    await snapshotFormLog(conn, formId, actorId(req), "DELETE");

    // Clear dependent mappings first to satisfy FK constraints.
    await conn.query("DELETE FROM ptw_location_forms_details WHERE form_id = ?", [formId]);
    await conn.query("DELETE FROM ptw_form_section_map WHERE form_id = ?", [formId]);
    await conn.query("DELETE FROM ptw_archive_form WHERE form_id = ?", [formId]);

    const [result] = await conn.query(
      "DELETE FROM ptw_form_details WHERE form_id = ?",
      [formId]
    );

    await conn.commit();
    res.json({ affectedRows: result.affectedRows });
  } catch (err) {
    await conn.rollback().catch(() => {});
    next(err);
  } finally {
    conn.release();
  }
}
