/**
 * 출하지시 컨트롤러 (shipment_instruction + shipment_instruction_detail)
 */
import { Response } from "express";
import { AuthenticatedRequest } from "../types/auth";
import { getPool } from "../database/db";
import { logger } from "../utils/logger";
import { numberingRuleService } from "../services/numberingRuleService";

// ─── 출하지시 목록 조회 ───
export async function getList(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { dateFrom, dateTo, status, customer, keyword } = req.query;

    const conditions: string[] = [];
    const params: any[] = [];
    let idx = 1;

    if (companyCode !== "*") {
      conditions.push(`si.company_code = $${idx}`);
      params.push(companyCode);
      idx++;
    }
    if (dateFrom) {
      conditions.push(`si.instruction_date >= $${idx}::date`);
      params.push(dateFrom);
      idx++;
    }
    if (dateTo) {
      conditions.push(`si.instruction_date <= $${idx}::date`);
      params.push(dateTo);
      idx++;
    }
    if (status) {
      conditions.push(`si.status = $${idx}`);
      params.push(status);
      idx++;
    }
    if (customer) {
      conditions.push(`(c.customer_name ILIKE $${idx} OR si.partner_id ILIKE $${idx})`);
      params.push(`%${customer}%`);
      idx++;
    }
    if (keyword) {
      conditions.push(`(si.instruction_no ILIKE $${idx} OR si.memo ILIKE $${idx})`);
      params.push(`%${keyword}%`);
      idx++;
    }

    const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";

    const query = `
      SELECT
        si.*,
        COALESCE(c.customer_name, si.partner_id, '') AS customer_name,
        COALESCE(
          json_agg(
            json_build_object(
              'id', sid.id,
              'item_code', sid.item_code,
              'item_name', COALESCE(i.item_name, sid.item_name, sid.item_code),
              'spec', sid.spec,
              'material', sid.material,
              'order_qty', sid.order_qty,
              'plan_qty', sid.plan_qty,
              'ship_qty', sid.ship_qty,
              'source_type', sid.source_type,
              'shipment_plan_id', sid.shipment_plan_id,
              'sales_order_id', sid.sales_order_id,
              'detail_id', sid.detail_id
            )
          ) FILTER (WHERE sid.id IS NOT NULL),
          '[]'
        ) AS items
      FROM shipment_instruction si
      LEFT JOIN customer_mng c
        ON si.partner_id = c.customer_code AND si.company_code = c.company_code
      LEFT JOIN shipment_instruction_detail sid
        ON si.id = sid.instruction_id AND si.company_code = sid.company_code
      LEFT JOIN LATERAL (
        SELECT item_name FROM item_info
        WHERE item_number = sid.item_code AND company_code = si.company_code
        LIMIT 1
      ) i ON true
      ${where}
      GROUP BY si.id, c.customer_name
      ORDER BY si.created_date DESC
    `;

    const pool = getPool();
    const result = await pool.query(query, params);

    logger.info("출하지시 목록 조회", { companyCode, count: result.rowCount });
    return res.json({ success: true, data: result.rows });
  } catch (error: any) {
    logger.error("출하지시 목록 조회 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 다음 출하지시번호 미리보기 ───
export async function previewNextNo(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    let instructionNo: string;

    try {
      const rule = await numberingRuleService.getNumberingRuleByColumn(
        companyCode, "shipment_instruction", "instruction_no"
      );
      if (rule) {
        instructionNo = await numberingRuleService.previewCode(
          rule.ruleId, companyCode, {}
        );
      } else {
        throw new Error("채번 규칙 없음");
      }
    } catch {
      const pool = getPool();
      const today = new Date().toISOString().split("T")[0].replace(/-/g, "");
      const seqRes = await pool.query(
        `SELECT COUNT(*) + 1 AS seq FROM shipment_instruction WHERE company_code = $1 AND instruction_no LIKE $2`,
        [companyCode, `SI-${today}-%`]
      );
      const seq = String(seqRes.rows[0].seq).padStart(3, "0");
      instructionNo = `SI-${today}-${seq}`;
    }

    return res.json({ success: true, instructionNo });
  } catch (error: any) {
    logger.error("출하지시번호 미리보기 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 출하지시 저장 (신규/수정) ───
export async function save(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const {
      id: editId,
      instructionDate,
      partnerId,
      status: orderStatus,
      memo,
      carrierName,
      vehicleNo,
      driverName,
      driverContact,
      arrivalTime,
      deliveryAddress,
      items,
    } = req.body;

    if (!instructionDate) {
      return res.status(400).json({ success: false, message: "출하지시일은 필수입니다" });
    }
    if (!items || items.length === 0) {
      return res.status(400).json({ success: false, message: "품목을 선택해주세요" });
    }

    const pool = getPool();
    const client = await pool.connect();

    try {
      await client.query("BEGIN");

      let instructionId: number;
      let instructionNo: string;

      if (editId) {
        // 수정
        const check = await client.query(
          `SELECT id, instruction_no FROM shipment_instruction WHERE id = $1 AND company_code = $2`,
          [editId, companyCode]
        );
        if (check.rowCount === 0) {
          throw new Error("출하지시를 찾을 수 없습니다");
        }
        instructionId = editId;
        instructionNo = check.rows[0].instruction_no;

        await client.query(
          `UPDATE shipment_instruction SET
            instruction_date = $1::date, partner_id = $2, status = $3, memo = $4,
            carrier_name = $5, vehicle_no = $6, driver_name = $7, driver_contact = $8,
            arrival_time = $9, delivery_address = $10,
            updated_date = NOW(), updated_by = $11
           WHERE id = $12 AND company_code = $13`,
          [
            instructionDate, partnerId, orderStatus || "READY", memo,
            carrierName, vehicleNo, driverName, driverContact,
            arrivalTime || null, deliveryAddress,
            userId, editId, companyCode,
          ]
        );

        // 기존 디테일 삭제 후 재삽입
        await client.query(
          `DELETE FROM shipment_instruction_detail WHERE instruction_id = $1 AND company_code = $2`,
          [editId, companyCode]
        );
      } else {
        // 신규 - 채번 규칙이 있으면 사용, 없으면 자체 생성
        try {
          const rule = await numberingRuleService.getNumberingRuleByColumn(
            companyCode, "shipment_instruction", "instruction_no"
          );
          if (rule) {
            instructionNo = await numberingRuleService.allocateCode(
              rule.ruleId, companyCode, { instruction_date: instructionDate }
            );
            logger.info("채번 규칙으로 출하지시번호 생성", { ruleId: rule.ruleId, instructionNo });
          } else {
            throw new Error("채번 규칙 없음 - 폴백");
          }
        } catch {
          const today = new Date().toISOString().split("T")[0].replace(/-/g, "");
          const seqRes = await client.query(
            `SELECT COUNT(*) + 1 AS seq FROM shipment_instruction WHERE company_code = $1 AND instruction_no LIKE $2`,
            [companyCode, `SI-${today}-%`]
          );
          const seq = String(seqRes.rows[0].seq).padStart(3, "0");
          instructionNo = `SI-${today}-${seq}`;
          logger.info("폴백으로 출하지시번호 생성", { instructionNo });
        }

        const insertRes = await client.query(
          `INSERT INTO shipment_instruction
            (company_code, instruction_no, instruction_date, partner_id, status, memo,
             carrier_name, vehicle_no, driver_name, driver_contact, arrival_time, delivery_address,
             created_date, created_by)
           VALUES ($1, $2, $3::date, $4, $5, $6, $7, $8, $9, $10, $11, $12, NOW(), $13)
           RETURNING id`,
          [
            companyCode, instructionNo, instructionDate, partnerId,
            orderStatus || "READY", memo,
            carrierName, vehicleNo, driverName, driverContact,
            arrivalTime || null, deliveryAddress, userId,
          ]
        );
        instructionId = insertRes.rows[0].id;
      }

      // 디테일 삽입
      for (const item of items) {
        await client.query(
          `INSERT INTO shipment_instruction_detail
            (company_code, instruction_id, shipment_plan_id, sales_order_id, detail_id,
             item_code, item_name, spec, material, order_qty, plan_qty, ship_qty,
             source_type, created_date, created_by)
           VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, NOW(), $14)`,
          [
            companyCode, instructionId,
            item.shipmentPlanId || null, item.salesOrderId || null, item.detailId || null,
            item.itemCode, item.itemName, item.spec, item.material,
            item.orderQty || 0, item.planQty || 0, item.shipQty || 0,
            item.sourceType || "shipmentPlan", userId,
          ]
        );
      }

      await client.query("COMMIT");

      logger.info("출하지시 저장 완료", { companyCode, instructionId, instructionNo, itemCount: items.length });
      return res.json({ success: true, data: { id: instructionId, instructionNo } });
    } catch (txErr) {
      await client.query("ROLLBACK");
      throw txErr;
    } finally {
      client.release();
    }
  } catch (error: any) {
    logger.error("출하지시 저장 실패", { error: error.message, stack: error.stack });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 출하지시 삭제 ───
export async function remove(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { ids } = req.body;

    if (!ids || !Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({ success: false, message: "삭제할 ID가 필요합니다" });
    }

    const pool = getPool();
    // CASCADE로 디테일도 자동 삭제
    const result = await pool.query(
      `DELETE FROM shipment_instruction WHERE id = ANY($1::int[]) AND company_code = $2 RETURNING id`,
      [ids, companyCode]
    );

    logger.info("출하지시 삭제", { companyCode, deletedCount: result.rowCount });
    return res.json({ success: true, deletedCount: result.rowCount });
  } catch (error: any) {
    logger.error("출하지시 삭제 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 출하계획 목록 (모달 왼쪽 패널용) ───
export async function getShipmentPlanSource(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { keyword, customer, page: pageStr, pageSize: pageSizeStr } = req.query;
    const page = Math.max(1, parseInt(pageStr as string) || 1);
    const pageSize = Math.min(100, Math.max(1, parseInt(pageSizeStr as string) || 20));
    const offset = (page - 1) * pageSize;

    const conditions = ["sp.company_code = $1", "sp.status = 'READY'"];
    const params: any[] = [companyCode];
    let idx = 2;

    if (keyword) {
      conditions.push(`(COALESCE(d.part_code, m.part_code, '') ILIKE $${idx} OR COALESCE(i.item_name, d.part_name, m.part_name, '') ILIKE $${idx})`);
      params.push(`%${keyword}%`);
      idx++;
    }
    if (customer) {
      conditions.push(`(c.customer_name ILIKE $${idx} OR COALESCE(m.partner_id, d.delivery_partner_code, '') ILIKE $${idx})`);
      params.push(`%${customer}%`);
      idx++;
    }

    const whereClause = conditions.join(" AND ");
    const fromClause = `
      FROM shipment_plan sp
      LEFT JOIN sales_order_detail d ON sp.detail_id = d.id AND sp.company_code = d.company_code
      LEFT JOIN sales_order_mng m ON sp.sales_order_id = m.id AND sp.company_code = m.company_code
      LEFT JOIN LATERAL (
        SELECT item_name FROM item_info
        WHERE item_number = COALESCE(d.part_code, m.part_code) AND company_code = sp.company_code
        LIMIT 1
      ) i ON true
      LEFT JOIN customer_mng c
        ON COALESCE(m.partner_id, d.delivery_partner_code) = c.customer_code AND sp.company_code = c.company_code
      WHERE ${whereClause}
    `;

    const pool = getPool();
    const countResult = await pool.query(`SELECT COUNT(*) AS total ${fromClause}`, params);
    const totalCount = parseInt(countResult.rows[0].total);

    const query = `
      SELECT
        sp.id, sp.plan_qty, sp.plan_date, sp.status, sp.shipment_plan_no,
        COALESCE(m.order_no, d.order_no, '') AS order_no,
        COALESCE(d.part_code, m.part_code, '') AS item_code,
        COALESCE(i.item_name, d.part_name, m.part_name, COALESCE(d.part_code, m.part_code, '')) AS item_name,
        COALESCE(d.spec, m.spec, '') AS spec,
        COALESCE(m.material, '') AS material,
        COALESCE(c.customer_name, m.partner_id, d.delivery_partner_code, '') AS customer_name,
        COALESCE(m.partner_id, d.delivery_partner_code, '') AS partner_code,
        sp.detail_id, sp.sales_order_id
      ${fromClause}
      ORDER BY sp.created_date DESC
      LIMIT $${idx} OFFSET $${idx + 1}
    `;
    params.push(pageSize, offset);

    const result = await pool.query(query, params);
    return res.json({ success: true, data: result.rows, totalCount, page, pageSize });
  } catch (error: any) {
    logger.error("출하계획 소스 조회 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 수주 목록 (모달 왼쪽 패널용) ───
export async function getSalesOrderSource(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { keyword, customer, page: pageStr, pageSize: pageSizeStr } = req.query;
    const page = Math.max(1, parseInt(pageStr as string) || 1);
    const pageSize = Math.min(100, Math.max(1, parseInt(pageSizeStr as string) || 20));
    const offset = (page - 1) * pageSize;

    const conditions = ["d.company_code = $1"];
    const params: any[] = [companyCode];
    let idx = 2;

    if (keyword) {
      conditions.push(`(d.part_code ILIKE $${idx} OR COALESCE(i.item_name, d.part_name, d.part_code) ILIKE $${idx} OR d.order_no ILIKE $${idx})`);
      params.push(`%${keyword}%`);
      idx++;
    }
    if (customer) {
      conditions.push(`(c.customer_name ILIKE $${idx} OR COALESCE(d.delivery_partner_code, m.partner_id, '') ILIKE $${idx})`);
      params.push(`%${customer}%`);
      idx++;
    }

    const whereClause = conditions.join(" AND ");
    const fromClause = `
      FROM sales_order_detail d
      LEFT JOIN sales_order_mng m ON d.order_no = m.order_no AND d.company_code = m.company_code
      LEFT JOIN LATERAL (
        SELECT item_name FROM item_info
        WHERE item_number = d.part_code AND company_code = d.company_code
        LIMIT 1
      ) i ON true
      LEFT JOIN customer_mng c
        ON COALESCE(d.delivery_partner_code, m.partner_id) = c.customer_code AND d.company_code = c.company_code
      WHERE ${whereClause}
    `;

    const pool = getPool();
    const countResult = await pool.query(`SELECT COUNT(*) AS total ${fromClause}`, params);
    const totalCount = parseInt(countResult.rows[0].total);

    const query = `
      SELECT
        d.id, d.order_no, d.part_code AS item_code,
        COALESCE(i.item_name, d.part_name, d.part_code) AS item_name,
        COALESCE(d.spec, '') AS spec, COALESCE(m.material, '') AS material,
        COALESCE(NULLIF(d.qty,'')::numeric, 0) AS qty,
        COALESCE(NULLIF(d.balance_qty,'')::numeric, 0) AS balance_qty,
        COALESCE(c.customer_name, COALESCE(d.delivery_partner_code, m.partner_id, '')) AS customer_name,
        COALESCE(d.delivery_partner_code, m.partner_id, '') AS partner_code,
        m.id AS master_id
      ${fromClause}
      ORDER BY d.created_date DESC
      LIMIT $${idx} OFFSET $${idx + 1}
    `;
    params.push(pageSize, offset);

    const result = await pool.query(query, params);
    return res.json({ success: true, data: result.rows, totalCount, page, pageSize });
  } catch (error: any) {
    logger.error("수주 소스 조회 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 품목 목록 (모달 왼쪽 패널용) ───
export async function getItemSource(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { keyword, page: pageStr, pageSize: pageSizeStr } = req.query;
    const page = Math.max(1, parseInt(pageStr as string) || 1);
    const pageSize = Math.min(100, Math.max(1, parseInt(pageSizeStr as string) || 20));
    const offset = (page - 1) * pageSize;

    const conditions = ["company_code = $1"];
    const params: any[] = [companyCode];
    let idx = 2;

    if (keyword) {
      conditions.push(`(item_number ILIKE $${idx} OR item_name ILIKE $${idx})`);
      params.push(`%${keyword}%`);
      idx++;
    }

    const whereClause = conditions.join(" AND ");

    const pool = getPool();
    const countResult = await pool.query(`SELECT COUNT(*) AS total FROM item_info WHERE ${whereClause}`, params);
    const totalCount = parseInt(countResult.rows[0].total);

    const query = `
      SELECT
        item_number AS item_code, item_name,
        COALESCE(size, '') AS spec, COALESCE(material, '') AS material
      FROM item_info
      WHERE ${whereClause}
      ORDER BY item_name
      LIMIT $${idx} OFFSET $${idx + 1}
    `;
    params.push(pageSize, offset);

    const result = await pool.query(query, params);
    return res.json({ success: true, data: result.rows, totalCount, page, pageSize });
  } catch (error: any) {
    logger.error("품목 소스 조회 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}
