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

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

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,
          qty: parseNumberOrNull(entry.qty ?? entry.quantity),
          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 [];
  }
};

const genNo = async (companyId, fiscalYearId = null) => {
  try {
    return await generateNextNumber(companyId, 'WELDING_VISUAL_INSPECTION_REPORT', null, fiscalYearId);
  } catch (_) {
    const y = new Date().getFullYear();
    const prefix = `WVIR${y}`;
    const last = await db.WeldingVisualInspectionReport.findOne({
      where: { company_id: companyId, wvir_no: { [Op.like]: `${prefix}%` } },
      order: [['wvir_no', 'DESC']]
    });
    const n = (parseInt(String(last?.wvir_no || '').slice(-4), 10) || 0) + 1;
    return `${prefix}${String(n).padStart(4, '0')}`;
  }
};

exports.getWorkOrderOptions = asyncHandler(async (req, res) => {
  const { companyId, plantId } = ctx(req);
  if (!companyId) return sendError(res, 'Company context is required', 400);
  const where = { company_id: companyId, ...(plantId ? { 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 }],
    order: [['id', 'DESC']]
  });
  return sendSuccess(res, 'Work order options fetched', rows.map((r) => ({
    id: r.id,
    work_order_number: r.work_order_number,
    item_code: r.finishedMaterial?.material_number || '',
    item_name: r.finishedMaterial?.description || '',
    work_order_qty: r.work_order_quantity || r.order_quantity || 0,
    label: `${r.work_order_number} | ${r.finishedMaterial?.material_number || '-'} | ${r.finishedMaterial?.description || '-'}`
  })));
});

exports.getWorkOrderPrefill = asyncHandler(async (req, res) => {
  const { workOrderId } = req.params;
  const { companyId, plantId } = ctx(req);
  const wo = await db.WorkOrder.findOne({
    where: { id: workOrderId, company_id: companyId, ...(plantId ? { plant_id: plantId } : {}) },
    include: [
      { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'name', 'legal_name'], required: false },
      { model: db.Material, as: 'finishedMaterial', attributes: ['id', 'material_number', 'description'], required: false },
      {
        model: db.Routing,
        as: 'routing',
        required: false,
        include: [{ model: db.RoutingOperation, as: 'operations', required: false }]
      }
    ]
  });
  if (!wo) return sendError(res, 'Work order not found', 404);

  const routingOps = Array.isArray(wo.routing?.operations) ? wo.routing.operations : [];
  const detailRows = [];
  const selectedMaterialIds = new Set();
  routingOps.forEach((op) => {
    parseOperationMaterialDetails(op.operation_materials_json)
      .filter((d) => Array.isArray(d.qa_menus) && d.qa_menus.includes('WELDING_VISUAL_INSPECTION'))
      .forEach((d) => {
        detailRows.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 rows = detailRows.map((d, i) => ({
    line_no: i + 1,
    sub_part_code: materialById.get(d.material_id)?.material_number || '',
    sub_part_name: materialById.get(d.material_id)?.description || '',
    qty: d.qty != null ? d.qty : 1,
    sample_1: '', sample_2: '', sample_3: '', sample_4: '', sample_5: '',
    sample_6: '', sample_7: '', sample_8: '', sample_9: '', sample_10: '',
    remark: '', line_status: ''
  }));

  const recentRouteCard = await db.RouteCard.findOne({
    where: { work_order_id: wo.id },
    include: [{ model: db.RouteCardOperation, as: 'operations', required: false, include: [{ model: db.ProductionOperator, as: 'operator', attributes: ['operator_name'], required: false }] }],
    order: [['id', 'DESC']]
  });
  const opWithUser = (recentRouteCard?.operations || []).find((o) => o?.operator?.operator_name);
  const today = new Date().toISOString().slice(0, 10);

  return sendSuccess(res, 'Work order prefill fetched', {
    work_order_id: wo.id,
    won: wo.work_order_number || '',
    item_code: wo.finishedMaterial?.material_number || '',
    item_name: wo.finishedMaterial?.description || '',
    customer_id: wo.customer?.id || null,
    customer_name: wo.customer?.name || wo.customer?.legal_name || '',
    work_order_qty: wo.work_order_quantity || wo.order_quantity || 0,
    sample_qty: wo.work_order_quantity || wo.order_quantity || 0,
    inspected_by: opWithUser?.operator?.operator_name || '',
    inspected_date: today,
    report_date: today,
    details_template: rows
  });
});

exports.getAll = asyncHandler(async (req, res) => {
  const { companyId, plantId } = ctx(req);
  const { page = 1, limit = 25, search = '', status } = req.query;
  const where = { company_id: companyId, ...(plantId ? { plant_id: plantId } : {}) };
  if (status) where.status = status;
  if (search) {
    where[Op.or] = [
      { wvir_no: { [Op.like]: `%${search}%` } },
      { won: { [Op.like]: `%${search}%` } },
      { item_code: { [Op.like]: `%${search}%` } },
      { item_name: { [Op.like]: `%${search}%` } },
      { customer_name: { [Op.like]: `%${search}%` } }
    ];
  }
  const offset = (parseInt(page, 10) - 1) * parseInt(limit, 10);
  const { count, rows } = await db.WeldingVisualInspectionReport.findAndCountAll({
    where,
    include: [{ model: db.BusinessPartner, as: 'customer', attributes: ['id', 'name'], required: false }],
    order: [['report_date', 'DESC'], ['id', 'DESC']],
    limit: parseInt(limit, 10),
    offset
  });
  return sendSuccess(res, 'Welding visual reports fetched', {
    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 row = await db.WeldingVisualInspectionReport.findByPk(req.params.id, {
    include: [
      { model: db.BusinessPartner, as: 'customer', attributes: ['id', 'name'], required: false },
      { model: db.WeldingVisualInspectionReportDetail, as: 'details', separate: true, order: [['line_no', 'ASC']] }
    ]
  });
  if (!row) return sendError(res, 'Welding visual report not found', 404);
  return sendSuccess(res, 'Welding visual report fetched', row);
});

exports.create = asyncHandler(async (req, res) => {
  const t = await db.sequelize.transaction();
  try {
    const userId = req.user?.id || null;
    const payload = req.body || {};
    const details = Array.isArray(payload.details) ? payload.details : [];
    if (!payload.wvir_no) payload.wvir_no = await genNo(payload.company_id, payload.fiscal_year_id);
    if (!payload.doc_no) payload.doc_no = 'RI/QA/F/10';
    const created = await db.WeldingVisualInspectionReport.create({ ...payload, created_by: userId, updated_by: userId }, { transaction: t });
    if (details.length) {
      await db.WeldingVisualInspectionReportDetail.bulkCreate(details.map((d, i) => ({ ...d, wvir_id: created.id, line_no: d.line_no || i + 1 })), { transaction: t });
    }
    await t.commit();
    const saved = await db.WeldingVisualInspectionReport.findByPk(created.id, { include: [{ model: db.WeldingVisualInspectionReportDetail, as: 'details', separate: true, order: [['line_no', 'ASC']] }] });
    return sendSuccess(res, 'Welding visual report created', saved, null, 201);
  } catch (e) {
    await t.rollback();
    return sendError(res, e.message || 'Failed to create welding visual report', 400);
  }
});

exports.update = asyncHandler(async (req, res) => {
  const t = await db.sequelize.transaction();
  try {
    const row = await db.WeldingVisualInspectionReport.findByPk(req.params.id);
    if (!row) {
      await t.rollback();
      return sendError(res, 'Welding visual report not found', 404);
    }
    const payload = req.body || {};
    const details = Array.isArray(payload.details) ? payload.details : [];
    await row.update({ ...payload, updated_by: req.user?.id || null }, { transaction: t });
    await db.WeldingVisualInspectionReportDetail.destroy({ where: { wvir_id: row.id }, transaction: t });
    if (details.length) {
      await db.WeldingVisualInspectionReportDetail.bulkCreate(details.map((d, i) => ({ ...d, wvir_id: row.id, line_no: d.line_no || i + 1 })), { transaction: t });
    }
    await t.commit();
    const saved = await db.WeldingVisualInspectionReport.findByPk(row.id, { include: [{ model: db.WeldingVisualInspectionReportDetail, as: 'details', separate: true, order: [['line_no', 'ASC']] }] });
    return sendSuccess(res, 'Welding visual report updated', saved);
  } catch (e) {
    await t.rollback();
    return sendError(res, e.message || 'Failed to update welding visual report', 400);
  }
});

exports.delete = asyncHandler(async (req, res) => {
  const t = await db.sequelize.transaction();
  try {
    const row = await db.WeldingVisualInspectionReport.findByPk(req.params.id);
    if (!row) {
      await t.rollback();
      return sendError(res, 'Welding visual report not found', 404);
    }
    await db.WeldingVisualInspectionReportDetail.destroy({ where: { wvir_id: row.id }, transaction: t });
    await row.destroy({ transaction: t });
    await t.commit();
    return sendSuccess(res, 'Welding visual report deleted');
  } catch (e) {
    await t.rollback();
    return sendError(res, e.message || 'Failed to delete welding visual report', 400);
  }
});

exports.updateStatus = asyncHandler(async (req, res) => {
  const { companyId, plantId } = ctx(req);
  const { id } = req.params;
  const status = String(req.body?.status || '').toUpperCase();
  if (!['DRAFT', 'SUBMITTED', 'APPROVED', 'CANCELLED'].includes(status)) {
    return sendError(res, 'Invalid status', 400);
  }
  const row = await db.WeldingVisualInspectionReport.findOne({
    where: {
      id,
      company_id: companyId,
      ...(plantId ? { plant_id: plantId } : {})
    }
  });
  if (!row) return sendError(res, 'Welding visual report not found', 404);
  await row.update({
    status,
    updated_by: req.user?.id || null
  });
  return sendSuccess(res, `Status updated to ${status}`, row);
});
