import { pool } from "../config/db.js";
import { ApiError } from "../utils/ApiError.js";
import { getJwtUserId } from "../middlewares/authMiddleware.js";
import {
  assertCurrentUserIsAssessmentExecutor,
  assertExecutorNotBlockedByPendingVerification
} from "../utils/oracAssessmentRoleRules.js";
import { isOracFormArchived } from "../utils/oracFormVersioning.js";

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

function normalizeAnswerQuestionId(raw) {
  if (typeof raw === "number" && Number.isInteger(raw) && raw > 0) {
    return raw;
  }
  const parsed = parseInt(String(raw ?? ""), 10);
  if (!Number.isInteger(parsed) || parsed <= 0) {
    return null;
  }
  return parsed;
}

async function inferFormIdFromQuestionIds(conn, questionIds) {
  const ids = [...new Set(questionIds.filter((n) => Number.isInteger(n) && n > 0))];
  if (!ids.length) {
    return null;
  }
  const [rows] = await conn.query(
    `SELECT MIN(fsm.form_id) AS form_id
     FROM orac_section_question_map sqm
     INNER JOIN orac_form_section_map fsm ON fsm.section_id = sqm.section_id
     WHERE sqm.question_id IN (?)`,
    [ids]
  );
  const raw = rows[0]?.form_id;
  if (raw == null || raw === "") {
    return null;
  }
  const n = Number(raw);
  return Number.isInteger(n) && n > 0 ? n : null;
}

export async function createSubmission(req, res, next) {
  const conn = await pool.getConnection();
  try {
    const assessmentId = toPositiveInteger(req.body?.assessment_id, "assessment_id");
    const snapId = toPositiveInteger(req.body?.snap_id, "snap_id");
    const submitterRaw = getJwtUserId(req.user);
    const submitterId = submitterRaw != null ? Number(submitterRaw) : NaN;
    if (!Number.isInteger(submitterId) || submitterId <= 0) {
      throw new ApiError(401, "Could not resolve submitter from token");
    }

    const answers = req.body?.answers;
    if (!Array.isArray(answers) || !answers.length) {
      throw new ApiError(400, "answers must be a non-empty array");
    }

    const normalizedAnswers = [];
    for (const ans of answers) {
      const qid = normalizeAnswerQuestionId(ans?.question_id);
      if (qid == null) {
        throw new ApiError(400, "Each answer must have a valid question_id");
      }
      if (ans.answer_value === undefined && ans.is_media === undefined) {
        throw new ApiError(400, `answer_value or is_media required for question_id ${qid}`);
      }
      normalizedAnswers.push({
        question_id: qid,
        is_media: ans.is_media,
        answer_value: ans.answer_value
      });
    }

    const [assessmentRows] = await conn.query(
      "SELECT `executor_id`, `assessment_status_id` FROM `orac_assessments` WHERE `assessment_id` = ? LIMIT 1",
      [assessmentId]
    );
    if (!assessmentRows.length) {
      throw new ApiError(404, "Assessment not found for provided assessment_id");
    }
    const assessmentRow = assessmentRows[0];
    assertCurrentUserIsAssessmentExecutor(
      submitterId,
      assessmentRow?.executor_id,
      req
    );
    assertExecutorNotBlockedByPendingVerification(req, assessmentRow);

    const [snapRows] = await conn.query(
      "SELECT 1 FROM `orac_snaps` WHERE `snap_id` = ? AND `assessment_id` = ? LIMIT 1",
      [snapId, assessmentId]
    );
    if (!snapRows.length) {
      throw new ApiError(
        404,
        "Snap not found for this assessment (check snap_id and assessment_id match the saved snap)"
      );
    }

    let formId = null;
    if (req.body?.form_id != null && req.body?.form_id !== "") {
      formId = toPositiveInteger(req.body.form_id, "form_id");
      const [formRows] = await conn.query(
        "SELECT 1 FROM `orac_form_details` WHERE `form_id` = ? LIMIT 1",
        [formId]
      );
      if (!formRows.length) {
        throw new ApiError(404, "Form not found for provided form_id");
      }
    } else {
      formId = await inferFormIdFromQuestionIds(
        conn,
        normalizedAnswers.map((a) => a.question_id)
      );
    }

    if (formId != null && (await isOracFormArchived(conn, formId))) {
      throw new ApiError(400, "This form version is archived; submissions must use the current form.");
    }

    await conn.beginTransaction();

    const [submissionResult] = await conn.query(
      `INSERT INTO orac_submissions
         (assessment_id, submitter_id, snap_id, form_id)
       VALUES (?, ?, ?, ?)`,
      [assessmentId, submitterId, snapId, formId]
    );

    const submissionId = submissionResult.insertId;

    const answerValues = normalizedAnswers.map((ans) => [
      submissionId,
      ans.question_id,
      ans.is_media ?? 0,
      ans.answer_value ?? null
    ]);

    await conn.query(
      `INSERT INTO orac_answers
         (submission_id, question_id, is_media, answer_value)
       VALUES ?`,
      [answerValues]
    );

    await conn.commit();

    return res.status(201).json({
      message: "Submission created successfully",
      data: {
        submission_id: submissionId,
        assessment_id: assessmentId,
        submitter_id: submitterId,
        snap_id: snapId,
        form_id: formId,
        answers_count: normalizedAnswers.length
      }
    });
  } catch (error) {
    await conn.rollback().catch(() => {});
    return next(error);
  } finally {
    conn.release();
  }
}
