import { pool } from "../config/db.js";
import { SWI_DOCUMENT_STATUS } from "../constants/swiDocumentStatus.js";
import { ApiError } from "../utils/ApiError.js";
import {
  copyPageToDocument,
  createDocumentPage,
  deleteDocumentPage,
  listDocumentPages,
  listIconsForSelect,
  loadAllPagesContentForDocument,
  loadPageContentForDocument,
  parsePageNum,
  reorderDocumentPages,
  savePageContent,
} from "./swiPageContentService.js";

let formDraftColumnReady = false;

export async function ensureFormDraftColumn() {
  if (formDraftColumnReady) return;
  const [cols] = await pool.query(
    "SHOW COLUMNS FROM `swi_document` LIKE 'form_draft'"
  );
  if (!cols.length) {
    await pool.query(
      "ALTER TABLE `swi_document` ADD COLUMN `form_draft` JSON NULL AFTER `completed_at`"
    );
  }
  formDraftColumnReady = true;
}

function locationName(row) {
  return row?.location_name ?? row?.Location_name ?? "";
}

function formatDate(value) {
  if (!value) return "";
  const d = value instanceof Date ? value : new Date(value);
  if (Number.isNaN(d.getTime())) return "";
  const dd = String(d.getDate()).padStart(2, "0");
  const mm = String(d.getMonth() + 1).padStart(2, "0");
  const yyyy = d.getFullYear();
  return `${dd}/${mm}/${yyyy}`;
}

function parseJsonDraft(raw) {
  if (raw == null || raw === "") return {};
  if (typeof raw === "object") return raw;
  try {
    return JSON.parse(raw);
  } catch {
    return {};
  }
}

/** Default form shape aligned with swi_DocTestingPages. */
export function buildFormFromDocument(row) {
  const pageCount = Number(row.page_count) || 1;
  return {
    margin_date: formatDate(row.issued_at) || formatDate(new Date()),
    margin_ref: row.document_id != null ? `Doc-${row.document_id}` : "",
    station: locationName(row) || "",
    job_area: row.job_area_name || "",
    document_no:
      row.document_number != null ? String(row.document_number) : "",
    issue_date: formatDate(row.issued_at),
    part_number: "",
    page: `1/${pageCount}`,
    description: row.document_name || "",
    process: row.process_name || "",
    materials_label: "",
    block1: "",
    block2: "",
    block3: "",
    show_logo_plus: true,
    logo_icon_id: "",
    logo2_icon_id: "",
    logo3_icon_id: "",
    block_icons: {
      "1": ["", "", "", "", "", ""],
      "2": ["", "", "", "", "", ""],
      "3": ["", "", "", "", "", ""],
    },
    footer_url: "https://www.v-swi.com/Details.aspx",
    footer_page: `1/${pageCount}`,
    image_slots: {},
  };
}

export function mergeFormDraft(base, draft) {
  const merged = { ...base, ...draft };
  if (draft?.image_slots && typeof draft.image_slots === "object") {
    merged.image_slots = { ...base.image_slots, ...draft.image_slots };
  }
  if (draft?.block_icons && typeof draft.block_icons === "object") {
    merged.block_icons = {
      ...base.block_icons,
      ...draft.block_icons,
    };
    for (const key of ["1", "2", "3"]) {
      if (Array.isArray(draft.block_icons[key])) {
        merged.block_icons[key] = draft.block_icons[key].map((id) =>
          id != null && id !== "" ? String(id) : ""
        );
      }
    }
  }
  return merged;
}

function resolveTotalPages(row, pages) {
  const fromDoc = Number(row.page_count) || 1;
  const fromMap = pages.length
    ? Math.max(...pages.map((p) => Number(p.page_num) || 0))
    : 0;
  return Math.max(fromDoc, fromMap, 1);
}

function mergePageContentIntoForm(form, pageContent, pageNum, totalPages) {
  return mergeFormDraft(form, {
    block1: pageContent.block1,
    block2: pageContent.block2,
    block3: pageContent.block3,
    image_slots: pageContent.image_slots || {},
    picture_canvas: pageContent.picture_canvas || [],
    block_icons: pageContent.block_icons || form.block_icons,
    block_logo_counts: pageContent.block_logo_counts || {},
    materials_label:
      pageContent.materials_label != null
        ? pageContent.materials_label
        : form.materials_label,
    page: `${pageNum}/${totalPages}`,
    footer_page: `${pageNum}/${totalPages}`,
    logo_icon_id:
      pageContent.block_icons?.["1"]?.[0] ||
      pageContent.block1_icon_id ||
      form.logo_icon_id ||
      "",
    logo2_icon_id:
      pageContent.block_icons?.["2"]?.[0] ||
      pageContent.block2_icon_id ||
      form.logo2_icon_id ||
      "",
    logo3_icon_id:
      pageContent.block_icons?.["3"]?.[0] ||
      pageContent.block3_icon_id ||
      form.logo3_icon_id ||
      "",
  });
}

const DOCUMENT_SELECT = `
  SELECT d.*,
         l.location_name,
         l.Location_name,
         ja.job_area_name,
         ds.document_status_name,
         dept.department_name
  FROM \`swi_document\` d
  LEFT JOIN \`locations\` l ON d.location_id = l.location_id
  LEFT JOIN \`swi_job_area\` ja ON d.job_area_id = ja.job_area_id
  LEFT JOIN \`swi_document_status\` ds ON d.document_status_id = ds.document_status_id
  LEFT JOIN \`departments\` dept ON d.department_id = dept.department_id
`;

export async function fetchDocumentEnriched(documentId, documentNumber) {
  await ensureFormDraftColumn();
  const [rows] = await pool.query(
    `${DOCUMENT_SELECT}
     WHERE d.document_id = ? AND d.document_number = ?
     LIMIT 1`,
    [documentId, documentNumber]
  );
  return rows[0] || null;
}

export async function listAssignmentsForUser(userId) {
  await ensureFormDraftColumn();
  const [rows] = await pool.query(
    `${DOCUMENT_SELECT}
     WHERE d.assigned_to_user_id = ?
     ORDER BY d.assigned_at DESC, d.document_id DESC, d.document_number DESC`,
    [userId]
  );
  return rows;
}

export async function fetchUserProfile(userId) {
  const [rows] = await pool.query(
    `SELECT u.user_id,
            u.employee_name,
            u.email_id,
            u.location_id,
            u.department_id,
            l.location_name,
            l.Location_name,
            dept.department_name
     FROM \`users\` u
     LEFT JOIN \`locations\` l ON u.location_id = l.location_id
     LEFT JOIN \`departments\` dept ON u.department_id = dept.department_id
     WHERE u.user_id = ?
     LIMIT 1`,
    [userId]
  );
  return rows[0] || null;
}

export async function listDocumentsForLocation(locationId) {
  await ensureFormDraftColumn();
  if (locationId == null || locationId === "") {
    return [];
  }
  const [rows] = await pool.query(
    `SELECT d.*,
            l.location_name,
            l.Location_name,
            ja.job_area_name,
            ds.document_status_name,
            dept.department_name,
            assignee.employee_name AS assignee_name
     FROM \`swi_document\` d
     LEFT JOIN \`locations\` l ON d.location_id = l.location_id
     LEFT JOIN \`swi_job_area\` ja ON d.job_area_id = ja.job_area_id
     LEFT JOIN \`swi_document_status\` ds ON d.document_status_id = ds.document_status_id
     LEFT JOIN \`departments\` dept ON d.department_id = dept.department_id
     LEFT JOIN \`users\` assignee ON d.assigned_to_user_id = assignee.user_id
     WHERE d.location_id = ?
     ORDER BY d.issued_at DESC, d.document_id DESC, d.document_number DESC`,
    [locationId]
  );
  return rows;
}

export async function getFormPayloadForUser(
  documentId,
  documentNumber,
  userId,
  pageNum = null
) {
  const row = await fetchDocumentEnriched(documentId, documentNumber);
  if (!row) return null;
  if (Number(row.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true, row };
  }

  const base = buildFormFromDocument(row);
  const draft = parseJsonDraft(row.form_draft);
  let form = mergeFormDraft(base, draft);

  const pages = await listDocumentPages(row.document_id);
  const totalPages = resolveTotalPages(row, pages);
  const targetPageNum =
    pageNum != null && Number(pageNum) > 0
      ? Number(pageNum)
      : parsePageNum(form.page, 1);

  const pageContent = await loadPageContentForDocument(
    row.document_id,
    targetPageNum
  );
  form = mergePageContentIntoForm(form, pageContent, targetPageNum, totalPages);

  const icons = await listIconsForSelect();

  const pageList =
    pages.length > 0
      ? pages.map((p) => ({
          page_id: p.page_id,
          page_num: p.page_num,
          page_header: p.page_header,
        }))
      : [
          {
            page_id: pageContent.page_id,
            page_num: targetPageNum,
            page_header: pageContent.page_header || "",
          },
        ];

  return {
    document: {
      document_id: row.document_id,
      document_number: row.document_number,
      document_name: row.document_name,
      document_status_id: row.document_status_id,
      document_status_name: row.document_status_name,
      assigned_at: row.assigned_at,
      issued_at: row.issued_at,
      page_count: totalPages,
      location_name: locationName(row),
      department_name: row.department_name,
      job_area_name: row.job_area_name,
      process_name: row.process_name,
    },
    pages: pageList,
    page: {
      page_id: pageContent.page_id,
      page_num: targetPageNum,
      page_header: pageContent.page_header || pageContent.materials_label,
    },
    icons,
    form,
    adminFields: [
      "margin_date",
      "margin_ref",
      "station",
      "job_area",
      "document_no",
      "issue_date",
      "description",
      "process",
    ],
  };
}

export async function createUserDocumentPage(
  documentId,
  documentNumber,
  userId
) {
  const row = await fetchDocumentEnriched(documentId, documentNumber);
  if (!row) return { notFound: true };
  if (Number(row.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true };
  }

  const page = await createDocumentPage(documentId, documentNumber);
  return getFormPayloadForUser(
    documentId,
    documentNumber,
    userId,
    page.page_num
  );
}

const USER_FORM_KEYS = new Set([
  "part_number",
  "page",
  "materials_label",
  "show_logo_plus",
  "footer_url",
  "footer_page",
  "logo_icon_id",
  "logo2_icon_id",
  "logo3_icon_id",
  "block_icons",
]);

export function pickUserFormDraft(body) {
  if (!body || typeof body !== "object") return {};
  const out = {};
  for (const [key, value] of Object.entries(body)) {
    if (USER_FORM_KEYS.has(key)) {
      out[key] = value;
    }
  }
  return out;
}

export async function saveUserFormDraft(documentId, documentNumber, userId, body) {
  const row = await fetchDocumentEnriched(documentId, documentNumber);
  if (!row) return { notFound: true };
  if (Number(row.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true };
  }

  const base = buildFormFromDocument(row);
  const existing = parseJsonDraft(row.form_draft);
  const patch = pickUserFormDraft(body?.form ?? body);
  const mergedDraft = mergeFormDraft(
    mergeFormDraft(base, existing),
    patch
  );

  await savePageContent(documentId, { ...mergedDraft, ...(body?.form ?? body) });

  const savedPageNum = parsePageNum(body?.form?.page ?? mergedDraft.page, 1);
  const [pageRows] = await pool.query(
    `SELECT COUNT(*) AS cnt FROM \`swi_document_page_map\` WHERE document_id = ?`,
    [documentId]
  );
  const mapCount = Number(pageRows[0]?.cnt || 0);
  if (mapCount > 0) {
    await pool.query(
      `UPDATE \`swi_document\`
       SET page_count = GREATEST(COALESCE(page_count, 1), ?, ?)
       WHERE document_id = ? AND document_number = ?`,
      [savedPageNum, mapCount, documentId, documentNumber]
    );
  }

  const userOnlyDraft = pickUserFormDraft(mergedDraft);
  const markComplete = Boolean(body?.mark_complete);

  if (markComplete) {
    const status = Number(row.document_status_id);
    if (
      status !== SWI_DOCUMENT_STATUS.IN_PROGRESS &&
      status !== SWI_DOCUMENT_STATUS.ASSIGNED
    ) {
      throw new ApiError(
        400,
        "Document can only be completed while in progress or assigned"
      );
    }
  }

  await pool.query(
    `UPDATE \`swi_document\`
     SET form_draft = ?${buildStatusSqlFragment(markComplete)}
     WHERE document_id = ? AND document_number = ?`,
    buildStatusUpdateParams(
      JSON.stringify(userOnlyDraft),
      markComplete,
      documentId,
      documentNumber
    )
  );

  return getFormPayloadForUser(
    documentId,
    documentNumber,
    userId,
    savedPageNum
  );
}

export async function reorderUserDocumentPages(
  documentId,
  documentNumber,
  userId,
  assignments
) {
  const row = await fetchDocumentEnriched(documentId, documentNumber);
  if (!row) return { notFound: true };
  if (Number(row.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true };
  }

  await reorderDocumentPages(documentId, assignments);

  const maxPageNum = assignments.reduce(
    (max, item) => Math.max(max, Number(item.page_num) || 0),
    0
  );
  if (maxPageNum > 0) {
    await pool.query(
      `UPDATE \`swi_document\`
       SET page_count = GREATEST(COALESCE(page_count, 1), ?)
       WHERE document_id = ? AND document_number = ?`,
      [maxPageNum, documentId, documentNumber]
    );
  }

  return getFormPayloadForUser(documentId, documentNumber, userId);
}

export async function deleteUserDocumentPage(
  documentId,
  documentNumber,
  userId,
  pageId
) {
  const row = await fetchDocumentEnriched(documentId, documentNumber);
  if (!row) return { notFound: true };
  if (Number(row.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true };
  }

  const numericPageId = Number(pageId);
  if (!Number.isInteger(numericPageId) || numericPageId < 1) {
    return { badRequest: true, message: "Invalid page_id" };
  }

  const [owned] = await pool.query(
    `SELECT page_id FROM \`swi_document_page_map\`
     WHERE document_id = ? AND page_id = ?
     LIMIT 1`,
    [documentId, numericPageId]
  );
  if (!owned.length) {
    return { badRequest: true, message: "Page not found in this document" };
  }

  try {
    await deleteDocumentPage(documentId, documentNumber, numericPageId);
  } catch (err) {
    if (err.code === "LAST_PAGE") {
      return { badRequest: true, message: err.message };
    }
    if (err.code === "PAGE_NOT_FOUND") {
      return { badRequest: true, message: "Page not found in this document" };
    }
    throw err;
  }

  const pages = await loadAllPagesContentForDocument(documentId);
  return {
    deleted_page_id: numericPageId,
    pages,
  };
}

function buildStatusSqlFragment(markComplete) {
  if (markComplete) {
    return `, \`document_status_id\` = ?, \`completed_at\` = COALESCE(\`completed_at\`, ?)`;
  }
  return "";
}

function buildStatusUpdateParams(
  formDraftJson,
  markComplete,
  documentId,
  documentNumber
) {
  const base = [formDraftJson];
  if (markComplete) {
    base.push(SWI_DOCUMENT_STATUS.COMPLETED, new Date());
  }
  base.push(documentId, documentNumber);
  return base;
}

export async function startUserDocumentWork(
  documentId,
  documentNumber,
  userId
) {
  const row = await fetchDocumentEnriched(documentId, documentNumber);
  if (!row) return { notFound: true };
  if (Number(row.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true };
  }

  const status = Number(row.document_status_id);
  if (status === SWI_DOCUMENT_STATUS.COMPLETED) {
    return { badTransition: true, message: "Document is already completed" };
  }
  if (status >= SWI_DOCUMENT_STATUS.IN_PROGRESS) {
    return getFormPayloadForUser(documentId, documentNumber, userId);
  }
  if (
    status !== SWI_DOCUMENT_STATUS.ASSIGNED &&
    status !== SWI_DOCUMENT_STATUS.OPENED &&
    status !== 0 &&
    !Number.isNaN(status)
  ) {
    return { badTransition: true, message: "Document cannot be started" };
  }

  await pool.query(
    `UPDATE \`swi_document\`
     SET \`document_status_id\` = ?
     WHERE \`document_id\` = ? AND \`document_number\` = ?`,
    [SWI_DOCUMENT_STATUS.IN_PROGRESS, documentId, documentNumber]
  );

  return getFormPayloadForUser(documentId, documentNumber, userId);
}

export async function copyUserDocumentPage(
  targetDocumentId,
  targetDocumentNumber,
  userId,
  sourcePageId
) {
  const targetRow = await fetchDocumentEnriched(
    targetDocumentId,
    targetDocumentNumber
  );
  if (!targetRow) return { notFound: true };
  if (Number(targetRow.assigned_to_user_id) !== Number(userId)) {
    return { forbidden: true };
  }

  const pageId = Number(sourcePageId);
  if (!Number.isInteger(pageId) || pageId < 1) {
    return { badRequest: true, message: "Invalid source_page_id" };
  }

  const [sourceAccess] = await pool.query(
    `SELECT pm.page_id, d.document_id, d.document_number
     FROM \`swi_document_page_map\` pm
     INNER JOIN \`swi_document\` d ON d.document_id = pm.document_id
     WHERE pm.page_id = ? AND d.assigned_to_user_id = ?
     LIMIT 1`,
    [pageId, userId]
  );
  if (!sourceAccess.length) {
    return { sourceForbidden: true };
  }

  const copied = await copyPageToDocument(
    pageId,
    targetDocumentId,
    targetDocumentNumber
  );
  const pages = await loadAllPagesContentForDocument(targetDocumentId);

  return {
    document_id: targetDocumentId,
    document_number: targetDocumentNumber,
    copied_page: copied,
    pages,
  };
}
