import { pool } from "../config/db.js";
import { ApiError } from "../utils/ApiError.js";
import { withInferredSectionType } from "../utils/sectionTypeInfer.js";

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 optionalOrder(value) {
  if (value === undefined || value === null || value === "") {
    return null;
  }
  const n = Number(value);
  if (!Number.isInteger(n) || n < 0) {
    throw new ApiError(400, "section_in_form_order must be a non-negative integer");
  }
  return n;
}

function optionalSkipable(value) {
  if (value === undefined || value === null || value === "") {
    return 0;
  }
  if (value === true || value === "true" || value === 1 || value === "1") {
    return 1;
  }
  if (value === false || value === "false" || value === 0 || value === "0") {
    return 0;
  }
  throw new ApiError(400, "skipable must be 0 or 1");
}

/** Form row plus sections linked via ptw_form_section_map. */
export async function getFormWithSections(req, res, next) {
  try {
    const formId = toPositiveInt(req.params.form_id, "form_id");

    const [formRows] = await pool.query(
      "SELECT * FROM `ptw_form_details` WHERE `form_id` = ? LIMIT 1",
      [formId]
    );
    if (!formRows.length) {
      throw new ApiError(404, "Form not found");
    }

    const [sections] = await pool.query(
      `SELECT
         fsm.section_id,
         fsm.section_in_form_order,
         fsm.skipable,
         s.title,
         s.section_parent_id,
         s.created_by,
         s.created_date
       FROM ptw_form_section_map fsm
       JOIN ptw_section_details s ON s.section_id = fsm.section_id
       WHERE fsm.form_id = ?
       ORDER BY COALESCE(fsm.section_in_form_order, 999999), fsm.section_id`,
      [formId]
    );

    res.json({
      ...formRows[0],
      sections: sections.map(withInferredSectionType)
    });
  } catch (err) {
    next(err);
  }
}

/** Insert into ptw_form_section_map with FK + duplicate checks. */
export async function createFormSectionMap(req, res, next) {
  try {
    const formId = toPositiveInt(req.body?.form_id, "form_id");
    const sectionId = toPositiveInt(req.body?.section_id, "section_id");
    let order = optionalOrder(req.body?.section_in_form_order);
    const skipable = optionalSkipable(req.body?.skipable);

    const [formRows] = await pool.query(
      "SELECT 1 FROM `ptw_form_details` WHERE `form_id` = ? LIMIT 1",
      [formId]
    );
    if (!formRows.length) {
      throw new ApiError(404, "Form not found for provided form_id");
    }

    const [sectionRows] = await pool.query(
      "SELECT 1 FROM `ptw_section_details` WHERE `section_id` = ? LIMIT 1",
      [sectionId]
    );
    if (!sectionRows.length) {
      throw new ApiError(404, "Section not found for provided section_id");
    }

    const [existingRows] = await pool.query(
      "SELECT 1 FROM `ptw_form_section_map` WHERE `form_id` = ? AND `section_id` = ? LIMIT 1",
      [formId, sectionId]
    );
    if (existingRows.length) {
      throw new ApiError(409, "Section is already mapped to this form");
    }

    if (order == null) {
      const [maxRows] = await pool.query(
        "SELECT COALESCE(MAX(`section_in_form_order`), 0) AS max_order FROM `ptw_form_section_map` WHERE `form_id` = ?",
        [formId]
      );
      order = Number(maxRows[0]?.max_order || 0) + 1;
    }

    await pool.query(
      "INSERT INTO `ptw_form_section_map` (`form_id`, `section_id`, `section_in_form_order`, `skipable`) VALUES (?, ?, ?, ?)",
      [formId, sectionId, order, skipable]
    );

    res.status(201).json({
      message: "Section mapped to form successfully",
      form_id: formId,
      section_id: sectionId,
      section_in_form_order: order,
      skipable
    });
  } catch (err) {
    next(err);
  }
}

/** Bulk update section_in_form_order for all sections mapped to a form. */
export async function reorderFormSections(req, res, next) {
  const conn = await pool.getConnection();
  try {
    const formId = toPositiveInt(req.params.form_id, "form_id");
    const sections = req.body?.sections;

    if (!Array.isArray(sections) || !sections.length) {
      throw new ApiError(400, "sections must be a non-empty array");
    }

    const [formRows] = await conn.query(
      "SELECT 1 FROM `ptw_form_details` WHERE `form_id` = ? LIMIT 1",
      [formId]
    );
    if (!formRows.length) {
      throw new ApiError(404, "Form not found");
    }

    const [mappedRows] = await conn.query(
      "SELECT `section_id` FROM `ptw_form_section_map` WHERE `form_id` = ?",
      [formId]
    );
    const mappedIds = new Set(mappedRows.map((r) => Number(r.section_id)));

    if (!mappedIds.size) {
      throw new ApiError(400, "This form has no mapped sections to reorder");
    }

    const payloadIds = new Set();
    const updates = [];

    for (let i = 0; i < sections.length; i += 1) {
      const row = sections[i] || {};
      const sectionId = toPositiveInt(row.section_id, `sections[${i}].section_id`);
      const order = optionalOrder(row.section_in_form_order);
      if (order == null) {
        throw new ApiError(
          400,
          `sections[${i}].section_in_form_order is required`
        );
      }
      if (!mappedIds.has(sectionId)) {
        throw new ApiError(
          400,
          `Section ${sectionId} is not mapped to form ${formId}`
        );
      }
      if (payloadIds.has(sectionId)) {
        throw new ApiError(400, `Duplicate section_id ${sectionId} in sections`);
      }
      payloadIds.add(sectionId);
      updates.push({ sectionId, order });
    }

    if (payloadIds.size !== mappedIds.size) {
      throw new ApiError(
        400,
        "sections must include every section mapped to this form"
      );
    }

    await conn.beginTransaction();

    for (const { sectionId, order } of updates) {
      await conn.query(
        "UPDATE `ptw_form_section_map` SET `section_in_form_order` = ? WHERE `form_id` = ? AND `section_id` = ?",
        [order, formId, sectionId]
      );
    }

    await conn.commit();

    res.json({
      message: "Section order updated successfully",
      form_id: formId,
      sections: updates.map(({ sectionId, order }) => ({
        section_id: sectionId,
        section_in_form_order: order
      }))
    });
  } catch (err) {
    try {
      await conn.rollback();
    } catch {
      /* ignore rollback failure */
    }
    next(err);
  } finally {
    conn.release();
  }
}
