const db = require('../models');
const { sendSuccess, sendError } = require('../utils/response');
const asyncHandler = require('../middlewares/asyncHandler');
const { Op } = require('sequelize');
const { generateNextNumber } = require('../services/numberFormatService');

const parseJSON = (value, fallback = []) => {
  if (!value) return fallback;
  if (Array.isArray(value)) return value;
  try {
    const parsed = JSON.parse(value);
    return Array.isArray(parsed) ? parsed : fallback;
  } catch {
    return fallback;
  }
};

const getCtx = (req) => ({
  companyId: req.user?.companyId || req.user?.company_id || req.query?.companyId || req.query?.company_id || req.body?.company_id,
  plantId: req.user?.plantId || req.user?.plant_id || req.query?.plantId || req.query?.plant_id || req.body?.plant_id
});

const generateReportNumber = async (companyId, fiscalYearId = null) => {
  try {
    return await generateNextNumber(companyId, 'ASSEMBLY_FINAL_INSPECTION_REPORT', null, fiscalYearId);
  } catch (e) {
    const year = new Date().getFullYear();
    const prefix = `AFIR${year}`;
    const last = await db.AssemblyFinalInspectionReport.findOne({
      where: { company_id: companyId, report_no: { [Op.like]: `${prefix}%` } },
      order: [['report_no', 'DESC']]
    });
    let seq = 1;
    if (last) {
      const n = parseInt(String(last.report_no).slice(-4), 10) || 0;
      seq = n + 1;
    }
    return `${prefix}${String(seq).padStart(4, '0')}`;
  }
};

const normalizeDetailRow = (row, reportId) => {
  const qtyRaw = row?.qty_per_set;
  const qtyIsEmpty = qtyRaw === '' || qtyRaw === null || qtyRaw === undefined;
  return {
    ...row,
    report_id: reportId,
    qty_per_set: qtyIsEmpty ? null : qtyRaw
  };
};

const parseNumberOrNull = (value) => {
  if (value === '' || value === null || value === undefined) return null;
  const n = Number(value);
  return Number.isFinite(n) ? n : null;
};

const parseOperationMaterialDetails = (jsonText) => {
  if (!jsonText) return [];
  try {
    const arr = JSON.parse(jsonText);
    if (!Array.isArray(arr)) return [];
    return arr
      .map((entry) => {
        if (!entry || typeof entry !== 'object') return null;
        const materialId = parseInt(entry.material_id || entry.materialId || entry.id || entry.value, 10);
        if (!Number.isFinite(materialId) || materialId <= 0) return null;
        return {
          material_id: materialId,
          width: parseNumberOrNull(entry.width),
          length: parseNumberOrNull(entry.length),
          height: parseNumberOrNull(entry.height),
          thickness: parseNumberOrNull(entry.thickness),
          qa_menus: Array.isArray(entry.qa_menus)
            ? Array.from(new Set(entry.qa_menus.map((m) => String(m || '').trim().toUpperCase()).filter(Boolean)))
            : []
        };
      })
      .filter(Boolean);
  } catch (_) {
    return [];
  }
};

exports.getWorkOrderOptions = asyncHandler(async (req, res) => {
  const { companyId, plantId } = getCtx(req);
  if (!companyId) return sendError(res, 'Company context is required', 400);

  const where = { company_id: companyId };
  if (plantId) where.plant_id = plantId;

  const rows = await db.WorkOrder.findAll({
    where,
    attributes: ['id', 'work_order_number', 'work_order_quantity', 'order_quantity'],
    include: [
      { model: db.Material, as: 'finishedMaterial', attributes: ['id', 'material_number', 'description'], required: false },
      { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'name', 'legal_name'], required: false }
    ],
    order: [['id', 'DESC']]
  });

  const data = rows.map((wo) => ({
    id: wo.id,
    work_order_number: wo.work_order_number,
    item_code: wo.finishedMaterial?.material_number || '',
    item_name: wo.finishedMaterial?.description || '',
    customer_name: wo.customer?.name || wo.customer?.legal_name || '',
    work_order_qty: wo.work_order_quantity || wo.order_quantity || 0,
    label: `${wo.work_order_number || ''} | ${wo.finishedMaterial?.material_number || '-'} | ${wo.finishedMaterial?.description || '-'}`
  }));

  return sendSuccess(res, 'Work order options fetched successfully', data);
});

exports.getWorkOrderPrefill = asyncHandler(async (req, res) => {
  const { workOrderId } = req.params;
  const { companyId, plantId } = getCtx(req);
  if (!companyId) return sendError(res, 'Company context is required', 400);

  const workOrder = await db.WorkOrder.findOne({
    where: {
      id: workOrderId,
      company_id: companyId,
      ...(plantId ? { plant_id: plantId } : {})
    },
    include: [
      { model: db.Material, as: 'finishedMaterial', attributes: ['id', 'material_number', 'description', 'revision_number'], required: false },
      { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'name', 'legal_name'], required: false },
      {
        model: db.Routing,
        as: 'routing',
        required: false,
        include: [{ model: db.RoutingOperation, as: 'operations', required: false }]
      }
    ]
  });

  if (!workOrder) return sendError(res, 'Work order not found', 404);

  const payload = {
    work_order_no: workOrder.work_order_number || '',
    customer_id: workOrder.customer?.id || null,
    customer_name: workOrder.customer?.name || workOrder.customer?.legal_name || '',
    model: workOrder.finishedMaterial?.material_number || '',
    item_revision_no: workOrder.finishedMaterial?.revision_number || '',
    total_qty: workOrder.work_order_quantity || workOrder.order_quantity || 0,
    report_date: workOrder.planned_start_date || new Date().toISOString().slice(0, 10),
    template_details: []
  };

  if (workOrder.finishedMaterial?.id) {
    const routingOps = Array.isArray(workOrder.routing?.operations) ? workOrder.routing.operations : [];
    const selectedRows = [];
    const selectedMaterialIds = new Set();
    routingOps.forEach((op) => {
      parseOperationMaterialDetails(op.operation_materials_json)
        .filter((d) => Array.isArray(d.qa_menus) && d.qa_menus.includes('ASSEMBLY_FINAL_INSPECTION'))
        .forEach((d) => {
          selectedRows.push(d);
          selectedMaterialIds.add(d.material_id);
        });
    });
    const materials = selectedMaterialIds.size > 0
      ? await db.Material.findAll({
        where: { id: { [Op.in]: Array.from(selectedMaterialIds) } },
        attributes: ['id', 'material_number', 'description']
      })
      : [];
    const materialById = new Map(materials.map((m) => [m.id, m]));

    const templateRows = [];
    const addTemplateRow = (item_description, specification = '', qty_per_set = '') => {
      templateRows.push({
        line_no: templateRows.length + 1,
        item_description,
        specification: specification == null ? '' : String(specification),
        qty_per_set
      });
    };

    const firstDim = selectedRows.find((r) =>
      r.length != null || r.width != null || r.height != null || r.thickness != null
    ) || null;
    if (firstDim?.length != null) addTemplateRow('Length', firstDim.length, '');
    if (firstDim?.width != null) addTemplateRow('Width', firstDim.width, '');
    if (firstDim?.height != null) addTemplateRow('Height', firstDim.height, '');
    if (firstDim?.thickness != null) addTemplateRow('Thickness', firstDim.thickness, '');

    if (templateRows.length === 0) {
      selectedRows.forEach((row) => {
        const material = materialById.get(row.material_id);
        addTemplateRow(
          `${material?.material_number || ''}${material?.description ? ` - ${material.description}` : ''}`.trim(),
          '',
          ''
        );
      });
    }

    // Add standard checklist rows to match hard-copy process format.
    const hasRow = (name) =>
      templateRows.some((r) => String(r.item_description || '').trim().toLowerCase() === name.toLowerCase());
    const addChecklistRow = (itemDescription, specification = '') => {
      if (!hasRow(itemDescription)) addTemplateRow(itemDescription, specification, '');
    };
    addChecklistRow('sharp Edges', 'SHANDER');
    addChecklistRow('visual', '');
    addChecklistRow('Eye Bolt', 'M10 / M12');
    addChecklistRow('Re- tapping', '');
    addChecklistRow('All parts are assembled as per drawing', '');
    addChecklistRow('packing', 'As per (p.o)');

    payload.template_details = templateRows;
  }

  return sendSuccess(res, 'Work order prefill fetched successfully', payload);
});

exports.getAll = asyncHandler(async (req, res) => {
  const { page = 1, limit = 25, search = '', company_id, plant_id, fiscal_year_id, customer_id, status, date_from, date_to } = req.query;
  const offset = (parseInt(page, 10) - 1) * parseInt(limit, 10);
  const where = {};

  if (company_id) where.company_id = company_id;
  if (plant_id) where.plant_id = plant_id;
  if (fiscal_year_id) where.fiscal_year_id = fiscal_year_id;
  if (customer_id) where.customer_id = customer_id;
  if (status) where.status = status;
  if (date_from && date_to) where.report_date = { [Op.between]: [date_from, date_to] };
  else if (date_from) where.report_date = { [Op.gte]: date_from };
  else if (date_to) where.report_date = { [Op.lte]: date_to };
  if (search) {
    where[Op.or] = [
      { report_no: { [Op.like]: `%${search}%` } },
      { work_order_no: { [Op.like]: `%${search}%` } },
      { customer_name: { [Op.like]: `%${search}%` } },
      { model: { [Op.like]: `%${search}%` } }
    ];
  }

  const { count, rows } = await db.AssemblyFinalInspectionReport.findAndCountAll({
    where,
    include: [
      { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
      { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
      { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
      { model: db.User, as: 'creator', attributes: ['id', 'username'] }
    ],
    limit: parseInt(limit, 10),
    offset,
    order: [['report_date', 'DESC'], ['id', 'DESC']],
    distinct: true
  });

  return sendSuccess(res, 'Assembly Final Inspection Reports fetched successfully', {
    items: rows,
    pagination: {
      total: count,
      page: parseInt(page, 10),
      limit: parseInt(limit, 10),
      pages: Math.ceil(count / parseInt(limit, 10))
    }
  });
});

exports.getById = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const report = await db.AssemblyFinalInspectionReport.findByPk(id, {
    include: [
      { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
      { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
      { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
      { model: db.User, as: 'chargeSignBy', attributes: ['id', 'username', 'first_name', 'last_name'] },
      { model: db.User, as: 'inspectedByUser', attributes: ['id', 'username', 'first_name', 'last_name'] },
      { model: db.AssemblyFinalInspectionReportDetail, as: 'details', order: [['line_no', 'ASC']] }
    ]
  });
  if (!report) return sendError(res, 'Assembly Final Inspection Report not found', 404);
  return sendSuccess(res, 'Report fetched successfully', report);
});

exports.create = asyncHandler(async (req, res) => {
  const userId = req.user?.id;
  const transaction = await db.sequelize.transaction();
  try {
    const { details = [], ...header } = req.body;
    if (!header.report_no) {
      header.report_no = await generateReportNumber(header.company_id, header.fiscal_year_id);
    }
    const report = await db.AssemblyFinalInspectionReport.create({
      ...header,
      created_by: userId,
      updated_by: userId
    }, { transaction });
    if (details.length > 0) {
      await db.AssemblyFinalInspectionReportDetail.bulkCreate(
        details.map((d) => normalizeDetailRow(d, report.id)),
        { transaction }
      );
    }
    await transaction.commit();
    const created = await db.AssemblyFinalInspectionReport.findByPk(report.id, {
      include: [
        { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
        { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
        { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'bp_number', 'name'] },
        { model: db.AssemblyFinalInspectionReportDetail, as: 'details', order: [['line_no', 'ASC']] }
      ]
    });
    return sendSuccess(res, 'Assembly Final Inspection Report created successfully', created, null, 201);
  } catch (err) {
    await transaction.rollback();
    throw err;
  }
});

exports.update = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const userId = req.user?.id;
  const report = await db.AssemblyFinalInspectionReport.findByPk(id);
  if (!report) return sendError(res, 'Assembly Final Inspection Report not found', 404);
  const transaction = await db.sequelize.transaction();
  try {
    const { details, ...header } = req.body;
    await report.update({ ...header, updated_by: userId }, { transaction });
    await db.AssemblyFinalInspectionReportDetail.destroy({ where: { report_id: id }, transaction });
    if (details && details.length > 0) {
      await db.AssemblyFinalInspectionReportDetail.bulkCreate(
        details.map((d) => normalizeDetailRow(d, parseInt(id, 10))),
        { transaction }
      );
    }
    await transaction.commit();
    const updated = await db.AssemblyFinalInspectionReport.findByPk(id, {
      include: [
        { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
        { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
        { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'bp_number', 'name'] },
        { model: db.AssemblyFinalInspectionReportDetail, as: 'details', order: [['line_no', 'ASC']] }
      ]
    });
    return sendSuccess(res, 'Assembly Final Inspection Report updated successfully', updated);
  } catch (err) {
    await transaction.rollback();
    throw err;
  }
});

exports.delete = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const report = await db.AssemblyFinalInspectionReport.findByPk(id);
  if (!report) return sendError(res, 'Assembly Final Inspection Report not found', 404);
  await report.destroy();
  return sendSuccess(res, 'Assembly Final Inspection Report deleted successfully');
});

exports.updateStatus = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const next = String(req.body?.status || '').toUpperCase();
  const allowed = ['DRAFT', 'SUBMITTED', 'APPROVED', 'CANCELLED'];
  if (!allowed.includes(next)) return sendError(res, 'Invalid status', 400);

  const row = await db.AssemblyFinalInspectionReport.findByPk(id);
  if (!row) return sendError(res, 'Assembly Final Inspection Report not found', 404);

  const current = String(row.status || '').toUpperCase();
  if (current === 'CANCELLED') return sendError(res, 'Cancelled report cannot be modified', 400);
  if (current === 'APPROVED' && next !== 'CANCELLED') return sendError(res, 'Approved report can only be cancelled', 400);
  if (current === next) return sendSuccess(res, 'Status unchanged', row);

  await row.update({ status: next, updated_by: req.user?.id || null });
  return sendSuccess(res, 'Status updated successfully', row);
});
