/**
 * 출하계획 컨트롤러
 *
 * 수주 마스터(sales_order_mng, INTEGER id) 또는
 * 수주 디테일(sales_order_detail, UUID id) 양쪽에서 호출 가능.
 *
 * ID 포맷으로 소스 테이블 자동 감지 → JOIN으로 완전한 정보 조합
 */

import { Response } from "express";
import { AuthenticatedRequest } from "../types/auth";
import { getPool } from "../database/db";
import { logger } from "../utils/logger";

// UUID 포맷 감지 (하이픈 포함 36자)
const isUUID = (val: string) =>
  /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
    val
  );

type SourceTable = "master" | "detail";

interface NormalizedOrder {
  sourceId: string; // 원본 ID (master: 정수, detail: UUID)
  masterId: number | null;
  detailId: string | null;
  orderNo: string;
  partCode: string;
  partName: string;
  partnerCode: string;
  partnerName: string;
  dueDate: string;
  orderQty: number;
  shipQty: number;
  balanceQty: number;
}

// ─── 소스 테이블 감지 ───

function detectSource(ids: string[]): SourceTable {
  if (ids.length === 0) return "detail";
  return ids.every((id) => isUUID(id)) ? "detail" : "master";
}

// ─── 수주 정보 정규화 (마스터/디테일 양쪽 JOIN) ───

async function getNormalizedOrders(
  companyCode: string,
  ids: string[],
  source: SourceTable
): Promise<NormalizedOrder[]> {
  const pool = getPool();

  if (source === "detail") {
    // 디테일 기준 → 마스터 JOIN (order_no), 거래처 JOIN (customer_mng)
    // item_info는 LATERAL로 1건만 매칭 (item_number 중복 대비)
    const res = await pool.query(
      `SELECT
        d.id AS detail_id,
        m.id AS master_id,
        d.order_no,
        d.part_code,
        COALESCE(d.part_name, i.item_name, d.part_code) AS part_name,
        COALESCE(d.delivery_partner_code, m.partner_id, '') AS partner_code,
        COALESCE(c.customer_name, d.delivery_partner_code, m.partner_id, '') AS partner_name,
        COALESCE(d.due_date, m.due_date::text, '') AS due_date,
        COALESCE(NULLIF(d.qty,'')::numeric, m.order_qty, 0) AS order_qty,
        COALESCE(NULLIF(d.ship_qty,'')::numeric, m.ship_qty, 0) AS ship_qty,
        COALESCE(NULLIF(d.balance_qty,'')::numeric, m.balance_qty, 0) AS balance_qty
       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 d.company_code = $1
         AND d.id = ANY($2::text[])`,
      [companyCode, ids]
    );

    return res.rows.map((r) => ({
      sourceId: r.detail_id,
      masterId: r.master_id,
      detailId: r.detail_id,
      orderNo: r.order_no || "",
      partCode: r.part_code || "",
      partName: r.part_name || "",
      partnerCode: r.partner_code || "",
      partnerName: r.partner_name || "",
      dueDate: r.due_date || "",
      orderQty: Number(r.order_qty || 0),
      shipQty: Number(r.ship_qty || 0),
      balanceQty: Number(r.balance_qty || 0),
    }));
  } else {
    // 마스터 기준 → 거래처 JOIN
    const numericIds = ids.map(Number).filter((n) => !isNaN(n));
    // item_info는 LATERAL로 1건만 매칭 (item_number 중복 대비)
    const res = await pool.query(
      `SELECT
        m.id AS master_id,
        NULL AS detail_id,
        m.order_no,
        m.part_code,
        COALESCE(m.part_name, i.item_name, m.part_code, '') AS part_name,
        COALESCE(m.partner_id, '') AS partner_code,
        COALESCE(c.customer_name, m.partner_id, '') AS partner_name,
        COALESCE(m.due_date::text, '') AS due_date,
        COALESCE(m.order_qty, 0) AS order_qty,
        COALESCE(m.ship_qty, 0) AS ship_qty,
        COALESCE(m.balance_qty, 0) AS balance_qty
       FROM sales_order_mng m
       LEFT JOIN LATERAL (
         SELECT item_name FROM item_info
         WHERE item_number = m.part_code AND company_code = m.company_code
         LIMIT 1
       ) i ON true
       LEFT JOIN customer_mng c
         ON m.partner_id = c.customer_code AND m.company_code = c.company_code
       WHERE m.company_code = $1
         AND m.id = ANY($2::int[])`,
      [companyCode, numericIds]
    );

    return res.rows.map((r) => ({
      sourceId: String(r.master_id),
      masterId: r.master_id,
      detailId: null,
      orderNo: r.order_no || "",
      partCode: r.part_code || "",
      partName: r.part_name || "",
      partnerCode: r.partner_code || "",
      partnerName: r.partner_name || "",
      dueDate: r.due_date || "",
      orderQty: Number(r.order_qty || 0),
      shipQty: Number(r.ship_qty || 0),
      balanceQty: Number(r.balance_qty || 0),
    }));
  }
}

// ─── 출하계획 목록 조회 (관리 화면용) ───

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 paramIndex = 1;

    // 멀티테넌시
    if (companyCode === "*") {
      // 최고 관리자: 전체 조회
    } else {
      conditions.push(`sp.company_code = $${paramIndex}`);
      params.push(companyCode);
      paramIndex++;
    }

    if (dateFrom) {
      conditions.push(`sp.plan_date >= $${paramIndex}::date`);
      params.push(dateFrom);
      paramIndex++;
    }
    if (dateTo) {
      conditions.push(`sp.plan_date <= $${paramIndex}::date`);
      params.push(dateTo);
      paramIndex++;
    }
    if (status) {
      conditions.push(`sp.status = $${paramIndex}`);
      params.push(status);
      paramIndex++;
    }
    if (customer) {
      conditions.push(`(c.customer_name ILIKE $${paramIndex} OR COALESCE(m.partner_id, d.delivery_partner_code, '') ILIKE $${paramIndex})`);
      params.push(`%${customer}%`);
      paramIndex++;
    }
    if (keyword) {
      conditions.push(`(
        COALESCE(m.order_no, d.order_no, '') ILIKE $${paramIndex}
        OR COALESCE(d.part_code, m.part_code, '') ILIKE $${paramIndex}
        OR COALESCE(i.item_name, d.part_name, m.part_name, '') ILIKE $${paramIndex}
        OR sp.shipment_plan_no ILIKE $${paramIndex}
      )`);
      params.push(`%${keyword}%`);
      paramIndex++;
    }

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

    const query = `
      SELECT
        sp.id,
        sp.plan_date,
        sp.plan_qty,
        sp.status,
        sp.memo,
        sp.shipment_plan_no,
        sp.created_date,
        sp.created_by,
        sp.detail_id,
        sp.sales_order_id,
        sp.remain_qty,
        COALESCE(m.order_no, d.order_no, '') AS order_no,
        COALESCE(d.part_code, m.part_code, '') AS part_code,
        COALESCE(i.item_name, d.part_name, m.part_name, COALESCE(d.part_code, m.part_code, '')) AS part_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,
        COALESCE(d.due_date, m.due_date::text, '') AS due_date,
        COALESCE(NULLIF(d.qty,'')::numeric, m.order_qty, 0) AS order_qty,
        COALESCE(NULLIF(d.ship_qty,'')::numeric, m.ship_qty, 0) AS shipped_qty
      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
      ${whereClause}
      ORDER BY sp.created_date DESC
    `;

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

    logger.info("출하계획 목록 조회", {
      companyCode,
      rowCount: result.rowCount,
    });

    return res.json({ success: true, data: result.rows });
  } catch (error: any) {
    logger.error("출하계획 목록 조회 실패", {
      error: error.message,
      stack: error.stack,
    });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 출하계획 단건 수정 ───

export async function updatePlan(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const { id } = req.params;
    const { planQty, planDate, memo } = req.body;

    const pool = getPool();

    const check = await pool.query(
      `SELECT id, status FROM shipment_plan WHERE id = $1 AND company_code = $2`,
      [id, companyCode]
    );

    if (check.rowCount === 0) {
      return res.status(404).json({ success: false, message: "출하계획을 찾을 수 없습니다" });
    }

    const setClauses: string[] = [];
    const updateParams: any[] = [];
    let idx = 1;

    if (planQty !== undefined) {
      setClauses.push(`plan_qty = $${idx}`);
      updateParams.push(planQty);
      idx++;
    }
    if (planDate !== undefined) {
      setClauses.push(`plan_date = $${idx}::date`);
      updateParams.push(planDate);
      idx++;
    }
    if (memo !== undefined) {
      setClauses.push(`memo = $${idx}`);
      updateParams.push(memo);
      idx++;
    }

    setClauses.push(`updated_date = NOW()`);
    setClauses.push(`updated_by = $${idx}`);
    updateParams.push(userId);
    idx++;

    updateParams.push(id);
    updateParams.push(companyCode);

    const updateQuery = `
      UPDATE shipment_plan
      SET ${setClauses.join(", ")}
      WHERE id = $${idx - 1} AND company_code = $${idx}
      RETURNING *
    `;

    // 파라미터 인덱스 수정
    const finalParams: any[] = [];
    let pIdx = 1;
    const setClausesFinal: string[] = [];

    if (planQty !== undefined) {
      setClausesFinal.push(`plan_qty = $${pIdx}`);
      finalParams.push(planQty);
      pIdx++;
    }
    if (planDate !== undefined) {
      setClausesFinal.push(`plan_date = $${pIdx}::date`);
      finalParams.push(planDate);
      pIdx++;
    }
    if (memo !== undefined) {
      setClausesFinal.push(`memo = $${pIdx}`);
      finalParams.push(memo);
      pIdx++;
    }
    setClausesFinal.push(`updated_date = NOW()`);
    setClausesFinal.push(`updated_by = $${pIdx}`);
    finalParams.push(userId);
    pIdx++;

    finalParams.push(id);
    finalParams.push(companyCode);

    const result = await pool.query(
      `UPDATE shipment_plan
       SET ${setClausesFinal.join(", ")}
       WHERE id = $${pIdx} AND company_code = $${pIdx + 1}
       RETURNING *`,
      finalParams
    );

    logger.info("출하계획 수정", { companyCode, planId: id, userId });

    return res.json({ success: true, data: result.rows[0] });
  } catch (error: any) {
    logger.error("출하계획 수정 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 품목별 집계 + 기존 출하계획 조회 ───

export async function getAggregate(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { ids } = req.query;

    if (!ids) {
      return res
        .status(400)
        .json({ success: false, message: "ids 파라미터가 필요합니다" });
    }

    const idList = (ids as string).split(",").filter(Boolean);
    if (idList.length === 0) {
      return res
        .status(400)
        .json({ success: false, message: "유효한 ID가 필요합니다" });
    }

    const source = detectSource(idList);
    logger.info("출하계획 집계 조회", {
      companyCode,
      source,
      idCount: idList.length,
    });

    // 1) 정규화된 수주 정보 조회 (JOIN 포함)
    const orders = await getNormalizedOrders(companyCode, idList, source);

    if (orders.length === 0) {
      return res
        .status(404)
        .json({ success: false, message: "해당 수주를 찾을 수 없습니다" });
    }

    // 2) 품목별 그룹핑
    const partCodeMap = new Map<string, NormalizedOrder[]>();
    for (const order of orders) {
      const key = order.partCode || "UNKNOWN";
      if (!partCodeMap.has(key)) partCodeMap.set(key, []);
      partCodeMap.get(key)!.push(order);
    }

    const pool = getPool();
    const result: Record<string, any> = {};

    for (const [partCode, partOrders] of partCodeMap) {
      // 총수주잔량: 선택된 수주들의 balance_qty 합
      const totalBalance = partOrders.reduce(
        (s, o) => s + (o.balanceQty > 0 ? o.balanceQty : o.orderQty - o.shipQty),
        0
      );

      // 기존 출하계획 조회 (detail_id 또는 sales_order_id 기준)
      let existingPlans: any[] = [];
      if (source === "detail") {
        const planDetailIds = partOrders
          .map((o) => o.detailId)
          .filter(Boolean);
        if (planDetailIds.length > 0) {
          const planRes = await pool.query(
            `SELECT id, detail_id, sales_order_id, plan_qty, plan_date,
                    shipment_plan_no, status
             FROM shipment_plan
             WHERE company_code = $1 AND detail_id = ANY($2::text[])
             ORDER BY created_date DESC`,
            [companyCode, planDetailIds]
          );
          existingPlans = planRes.rows.map((r) => ({
            id: r.id,
            sourceId: r.detail_id,
            planQty: Number(r.plan_qty || 0),
            planDate: r.plan_date,
            shipmentPlanNo: r.shipment_plan_no,
            status: r.status,
          }));
        }
      } else {
        const planMasterIds = partOrders
          .map((o) => o.masterId)
          .filter((id): id is number => id != null);
        if (planMasterIds.length > 0) {
          const planRes = await pool.query(
            `SELECT id, sales_order_id, detail_id, plan_qty, plan_date,
                    shipment_plan_no, status
             FROM shipment_plan
             WHERE company_code = $1 AND sales_order_id = ANY($2::int[])
             ORDER BY created_date DESC`,
            [companyCode, planMasterIds]
          );
          existingPlans = planRes.rows.map((r) => ({
            id: r.id,
            sourceId: String(r.sales_order_id),
            planQty: Number(r.plan_qty || 0),
            planDate: r.plan_date,
            shipmentPlanNo: r.shipment_plan_no,
            status: r.status,
          }));
        }
      }

      const totalPlanQty = existingPlans.reduce((s, p) => s + p.planQty, 0);

      // 현재고
      const stockRes = await pool.query(
        `SELECT COALESCE(SUM(current_qty::numeric), 0) AS current_stock
         FROM inventory_stock
         WHERE company_code = $1 AND item_code = $2`,
        [companyCode, partCode]
      );
      const currentStock = Number(stockRes.rows[0]?.current_stock || 0);

      // 생산중수량
      const prodRes = await pool.query(
        `SELECT COALESCE(SUM(plan_qty - COALESCE(completed_qty, 0)), 0) AS in_production
         FROM production_plan_mng
         WHERE company_code = $1
           AND item_code = $2
           AND status IN ('in_progress', 'planned')`,
        [companyCode, partCode]
      );
      const inProductionQty = Number(prodRes.rows[0]?.in_production || 0);

      result[partCode] = {
        totalBalance,
        totalPlanQty,
        currentStock,
        availableStock: currentStock - totalPlanQty,
        inProductionQty,
        existingPlans,
        orders: partOrders.map((o) => ({
          sourceId: o.sourceId,
          orderNo: o.orderNo,
          partCode: o.partCode,
          partName: o.partName,
          partnerName: o.partnerName,
          dueDate: o.dueDate,
          orderQty: o.orderQty,
          shipQty: o.shipQty,
          balanceQty: o.balanceQty,
        })),
      };
    }

    logger.info("출하계획 집계 조회 완료", {
      companyCode,
      source,
      partCodes: Array.from(partCodeMap.keys()),
      orderCount: orders.length,
    });

    return res.json({ success: true, data: result, source });
  } catch (error: any) {
    logger.error("출하계획 집계 조회 실패", {
      error: error.message,
      stack: error.stack,
    });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// ─── 출하계획 일괄 저장 ───

export async function batchSave(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const { plans, source } = req.body;

    if (!Array.isArray(plans) || plans.length === 0) {
      return res.status(400).json({
        success: false,
        message: "저장할 출하계획 데이터가 필요합니다",
      });
    }

    // source 자동 감지 (프론트에서 전달, 또는 ID 포맷으로 추론)
    const detectedSource: SourceTable =
      source || detectSource(plans.map((p: any) => String(p.sourceId)));

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

    try {
      await client.query("BEGIN");
      const savedPlans = [];

      for (const plan of plans) {
        const { sourceId, planQty, planDate } = plan;
        if (!sourceId || !planQty || planQty <= 0) continue;
        const planDateValue = planDate || null;

        if (detectedSource === "detail") {
          // 디테일 소스: detail_id로 저장
          const detailCheck = await client.query(
            `SELECT d.id, d.order_no, d.part_code, d.qty, d.ship_qty, d.balance_qty,
                    m.id AS master_id
             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
             WHERE d.id = $1 AND d.company_code = $2`,
            [sourceId, companyCode]
          );

          if (detailCheck.rowCount === 0) {
            throw new Error(`수주상세 ${sourceId}을 찾을 수 없습니다`);
          }

          const detail = detailCheck.rows[0];
          const qty = Number(detail.qty || 0);
          const shipQty = Number(detail.ship_qty || 0);
          const balanceQty = detail.balance_qty
            ? Number(detail.balance_qty)
            : qty - shipQty;

          if (balanceQty > 0 && planQty > balanceQty) {
            throw new Error(
              `수주번호 ${detail.order_no}: 출하계획량(${planQty})이 미출하량(${balanceQty})을 초과합니다`
            );
          }

          const insertRes = await client.query(
            `INSERT INTO shipment_plan
              (company_code, detail_id, sales_order_id, plan_qty, plan_date, status, created_by)
             VALUES ($1, $2, $3, $4, COALESCE($5::date, CURRENT_DATE), 'READY', $6)
             RETURNING *`,
            [companyCode, sourceId, detail.master_id, planQty, planDateValue, userId]
          );
          savedPlans.push(insertRes.rows[0]);

          // detail ship_qty 업데이트
          await client.query(
            `UPDATE sales_order_detail
             SET ship_qty = (COALESCE(NULLIF(ship_qty,'')::numeric, 0) + $1)::text,
                 balance_qty = (COALESCE(NULLIF(qty,'')::numeric, 0)
                   - COALESCE(NULLIF(ship_qty,'')::numeric, 0) - $1)::text,
                 updated_date = NOW()
             WHERE id = $2 AND company_code = $3`,
            [planQty, sourceId, companyCode]
          );
        } else {
          // 마스터 소스: sales_order_id로 저장
          const masterId = Number(sourceId);
          const masterCheck = await client.query(
            `SELECT id, order_no, order_qty, ship_qty, balance_qty
             FROM sales_order_mng
             WHERE id = $1 AND company_code = $2`,
            [masterId, companyCode]
          );

          if (masterCheck.rowCount === 0) {
            throw new Error(`수주 ID ${masterId}을 찾을 수 없습니다`);
          }

          const master = masterCheck.rows[0];
          const balanceQty = Number(master.balance_qty || 0);

          if (balanceQty > 0 && planQty > balanceQty) {
            throw new Error(
              `수주번호 ${master.order_no}: 출하계획량(${planQty})이 미출하량(${balanceQty})을 초과합니다`
            );
          }

          const insertRes = await client.query(
            `INSERT INTO shipment_plan
              (company_code, sales_order_id, plan_qty, plan_date, status, created_by)
             VALUES ($1, $2, $3, COALESCE($4::date, CURRENT_DATE), 'READY', $5)
             RETURNING *`,
            [companyCode, masterId, planQty, planDateValue, userId]
          );
          savedPlans.push(insertRes.rows[0]);

          // 마스터 ship_qty 업데이트
          await client.query(
            `UPDATE sales_order_mng
             SET ship_qty = COALESCE(ship_qty, 0) + $1,
                 balance_qty = COALESCE(order_qty, 0) - COALESCE(ship_qty, 0) - $1,
                 updated_date = NOW()
             WHERE id = $2 AND company_code = $3`,
            [planQty, masterId, companyCode]
          );
        }
      }

      await client.query("COMMIT");

      logger.info("출하계획 일괄 저장 완료", {
        companyCode,
        source: detectedSource,
        savedCount: savedPlans.length,
        userId,
      });

      return res.json({
        success: true,
        message: `${savedPlans.length}건 저장 완료`,
        data: savedPlans,
      });
    } catch (txError) {
      await client.query("ROLLBACK");
      throw txError;
    } finally {
      client.release();
    }
  } catch (error: any) {
    logger.error("출하계획 일괄 저장 실패", {
      error: error.message,
      stack: error.stack,
    });
    return res.status(500).json({ success: false, message: error.message });
  }
}
