import { ApiError } from "../utils/ApiError.js";
import { getJwtUserId } from "../middlewares/authMiddleware.js";
import {
  createUserDocumentPage,
  copyUserDocumentPage,
  deleteUserDocumentPage,
  fetchDocumentEnriched,
  fetchUserProfile,
  getFormPayloadForUser,
  listAssignmentsForUser,
  listDocumentsForLocation,
  reorderUserDocumentPages,
  saveUserFormDraft,
  startUserDocumentWork,
} from "../services/swiFormService.js";
import { loadAllPagesContentForDocument, listIconsForSelect } from "../services/swiPageContentService.js";

function requireUserId(req) {
  const userId = getJwtUserId(req.user);
  if (!userId) {
    throw new ApiError(401, "User id missing from token");
  }
  return Number(userId);
}

function parseDocParams(req) {
  const documentId = Number(req.params.document_id);
  const documentNumber = Number(req.params.document_number);
  if (!Number.isInteger(documentId) || !Number.isInteger(documentNumber)) {
    throw new ApiError(400, "Invalid document_id or document_number");
  }
  return { documentId, documentNumber };
}

function mapAssignmentRow(row) {
  return {
    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_to_user_id: row.assigned_to_user_id,
    assigned_at: row.assigned_at,
    issued_at: row.issued_at,
    completed_at: row.completed_at,
    page_count: row.page_count,
    process_name: row.process_name,
    location_id: row.location_id,
    location_name: row.location_name ?? row.Location_name ?? null,
    department_name: row.department_name ?? null,
    job_area_name: row.job_area_name ?? null,
    assignee_name: row.assignee_name ?? null,
  };
}

export async function getMyProfile(req, res, next) {
  try {
    const userId = requireUserId(req);
    const profile = await fetchUserProfile(userId);
    if (!profile) {
      throw new ApiError(404, "User not found");
    }
    res.json({
      user_id: profile.user_id,
      employee_name: profile.employee_name,
      email_id: profile.email_id,
      location_id: profile.location_id,
      location_name:
        profile.location_name ?? profile.Location_name ?? null,
      department_id: profile.department_id,
      department_name: profile.department_name ?? null,
    });
  } catch (err) {
    next(err);
  }
}

export async function listMyLocationDocuments(req, res, next) {
  try {
    const userId = requireUserId(req);
    const profile = await fetchUserProfile(userId);
    if (!profile) {
      throw new ApiError(404, "User not found");
    }

    const locationId = profile.location_id;
    const locationName =
      profile.location_name ?? profile.Location_name ?? null;

    if (locationId == null || locationId === "") {
      return res.json({
        location_id: null,
        location_name: null,
        documents: [],
      });
    }

    const rows = await listDocumentsForLocation(locationId);
    res.json({
      location_id: locationId,
      location_name: locationName,
      documents: rows.map(mapAssignmentRow),
    });
  } catch (err) {
    next(err);
  }
}

export async function listMyAssignments(req, res, next) {
  try {
    const userId = requireUserId(req);
    const rows = await listAssignmentsForUser(userId);
    res.json(rows.map(mapAssignmentRow));
  } catch (err) {
    next(err);
  }
}

export async function getMyAssignmentForm(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const pageNumRaw = req.query.page_num;
    const pageNum =
      pageNumRaw != null && pageNumRaw !== ""
        ? Number(pageNumRaw)
        : null;
    if (pageNum != null && (!Number.isInteger(pageNum) || pageNum < 1)) {
      throw new ApiError(400, "Invalid page_num");
    }

    const payload = await getFormPayloadForUser(
      documentId,
      documentNumber,
      userId,
      pageNum
    );

    if (!payload) {
      throw new ApiError(404, "Assignment not found");
    }
    if (payload.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }

    res.json(payload);
  } catch (err) {
    next(err);
  }
}

export async function getMyAssignmentDocumentPages(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const row = await fetchDocumentEnriched(documentId, documentNumber);

    if (!row) {
      throw new ApiError(404, "Assignment not found");
    }
    if (Number(row.assigned_to_user_id) !== userId) {
      throw new ApiError(403, "This document is not assigned to you");
    }

    const pages = await loadAllPagesContentForDocument(documentId);
    const icons = await listIconsForSelect();
    res.json({
      document_id: documentId,
      document_number: documentNumber,
      document_name: row.document_name,
      issued_at: row.issued_at ?? null,
      location_name: row.location_name ?? row.Location_name ?? null,
      job_area_name: row.job_area_name ?? null,
      process_name: row.process_name ?? null,
      form_draft: row.form_draft ?? null,
      page_count: row.page_count ?? null,
      pages,
      icons,
    });
  } catch (err) {
    next(err);
  }
}

export async function createMyAssignmentPage(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const result = await createUserDocumentPage(
      documentId,
      documentNumber,
      userId
    );

    if (result?.notFound) {
      throw new ApiError(404, "Assignment not found");
    }
    if (result?.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }

    res.status(201).json(result);
  } catch (err) {
    next(err);
  }
}

export async function deleteMyAssignmentPage(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const pageId = Number(req.params.page_id);

    if (!Number.isInteger(pageId) || pageId < 1) {
      throw new ApiError(400, "Invalid page_id");
    }

    const result = await deleteUserDocumentPage(
      documentId,
      documentNumber,
      userId,
      pageId
    );

    if (result?.notFound) {
      throw new ApiError(404, "Assignment not found");
    }
    if (result?.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }
    if (result?.badRequest) {
      throw new ApiError(400, result.message || "Invalid request");
    }

    res.json(result);
  } catch (err) {
    next(err);
  }
}

export async function saveMyAssignmentForm(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const result = await saveUserFormDraft(
      documentId,
      documentNumber,
      userId,
      req.body
    );

    if (result?.notFound) {
      throw new ApiError(404, "Assignment not found");
    }
    if (result?.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }

    res.json(result);
  } catch (err) {
    next(err);
  }
}

export async function reorderMyAssignmentPages(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const pages = req.body?.pages;

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

    for (const row of pages) {
      const pageId = Number(row?.page_id);
      const pageNum = Number(row?.page_num);
      if (!Number.isInteger(pageId) || pageId < 1) {
        throw new ApiError(400, "Each page must have a valid page_id");
      }
      if (!Number.isInteger(pageNum) || pageNum < 1) {
        throw new ApiError(400, "Each page must have a valid page_num");
      }
    }

    const result = await reorderUserDocumentPages(
      documentId,
      documentNumber,
      userId,
      pages
    );

    if (result?.notFound) {
      throw new ApiError(404, "Assignment not found");
    }
    if (result?.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }

    res.json(result);
  } catch (err) {
    next(err);
  }
}

export async function startMyAssignmentWork(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const result = await startUserDocumentWork(
      documentId,
      documentNumber,
      userId
    );

    if (result?.notFound) {
      throw new ApiError(404, "Assignment not found");
    }
    if (result?.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }
    if (result?.badTransition) {
      throw new ApiError(400, result.message || "Invalid status transition");
    }

    res.json(result);
  } catch (err) {
    next(err);
  }
}

export async function copyMyAssignmentPage(req, res, next) {
  try {
    const userId = requireUserId(req);
    const { documentId, documentNumber } = parseDocParams(req);
    const sourcePageId = Number(req.body?.source_page_id);

    if (!Number.isInteger(sourcePageId) || sourcePageId < 1) {
      throw new ApiError(400, "source_page_id is required");
    }

    const result = await copyUserDocumentPage(
      documentId,
      documentNumber,
      userId,
      sourcePageId
    );

    if (result?.notFound) {
      throw new ApiError(404, "Assignment not found");
    }
    if (result?.forbidden) {
      throw new ApiError(403, "This document is not assigned to you");
    }
    if (result?.sourceForbidden) {
      throw new ApiError(403, "Source page is not from your assignments");
    }
    if (result?.badRequest) {
      throw new ApiError(400, result.message || "Invalid request");
    }

    res.status(201).json(result);
  } catch (err) {
    next(err);
  }
}
