// ============================================================
// 구매관리 > 입고관리 — 입고등록 / 마감정보입력 / 매입마감
//
// wace_plm 1:1 이식:
//   - 입고등록 팝업: purchaseOrder/deliveryAcceptanceFormPopUp_new.do
//   - 입고 저장:    purchaseOrder/saveDeliveryInfo.do
//                   → supplyChainMgmt.saveDeliveryInfo (ARRIVAL_PLAN UPSERT)
//   - 마감정보:    purchaseOrder/saveArrivalPlanDeadlineInfo.do
//                   → purchaseOrder.saveArrivalPlanDeadlineInfo (8필드 조건부 UPDATE)
//   - 매입마감:    purchaseOrder/purchaseCloseByArrival.do
//                   → purchaseOrder.updateArrivalPlanCloseDate (PURCHASE_CLOSE_DATE)
//
// RPS 단순화:
//   - 동시발주(MULTI_YN), inventory_mgmt 동기, ERROR_QTY/ERROR_REASON 흐름은 추후
//   - 입고등록은 ARRIVAL_PLAN UPSERT 만 처리 (자재 신규/입고 이력은 차후 도메인)
// ============================================================

import { getPool } from "../database/db";
import { logger } from "../utils/logger";
import { createObjId } from "../utils/objidUtil";

export interface InboundFormInitResult {
  master: {
    pom_objid: string;
    purchase_order_no: string;
    project_no: string;
    contract_mgmt_objid: string;
    partner_objid: string;
    partner_name: string;
    delivery_status: string;
  };
  /** 발주 품목 — 입고 등록 팝업 좌측 그리드 */
  parts: {
    order_part_objid: string;
    part_objid: string;
    part_no: string;
    part_name: string;
    spec: string;
    maker: string;
    unit_title: string;
    order_qty: number;
    arrival_qty: number;        // 이미 입고된 수량
    non_arrival_qty: number;    // 미입고 수량 (order_qty - arrival_qty)
    delivery_request_date: string;
  }[];
  /** 기존 입고 차수 — 팝업 우측 그리드 */
  arrivals: {
    objid: string;
    order_part_objid: string;
    part_objid: string;
    group_seq: string;
    seq: string;
    receipt_date: string;
    location: string;
    sub_location: string;
    receipt_qty: number;
    arrival_qty: number;
    inventory_status: string;
  }[];
}

export interface InboundSaveRow {
  objid?: string;
  parent_objid: string;       // PURCHASE_ORDER_MASTER objid
  order_part_objid: string;
  part_objid: string;         // bigint as string OK
  group_seq: string;
  seq: string;
  receipt_date: string;
  location: string;
  sub_location: string;
  receipt_qty: number;
  arrival_qty: number;
  arrival_plan_date?: string;
}

export interface DeadlineInfoBody {
  objIds: string[];
  taxType: string;
  taxInvoiceDate?: string;
  exportDeclNo?: string;
  loadingDate?: string;
  foreignType?: string;
  duty?: string;
  importVat?: string;
  exchangeRate?: string;
}

/** GET /api/purchase/inbound-form/:pomObjid — 입고등록 팝업 자동채움 */
export async function getInboundFormInit(pomObjid: string): Promise<InboundFormInitResult | null> {
  const pool = getPool();
  try {
    const m = await pool.query(
      `SELECT POM.OBJID                              AS pom_objid,
              POM.PURCHASE_ORDER_NO                  AS purchase_order_no,
              POM.CONTRACT_MGMT_OBJID                AS contract_mgmt_objid,
              POM.PARTNER_OBJID                      AS partner_objid,
              CM.PROJECT_NO                          AS project_no,
              C.CLIENT_NM                            AS partner_name,
              COALESCE(POM.RECEPTION_STATUS, '')     AS delivery_status
         FROM PURCHASE_ORDER_MASTER POM
         LEFT JOIN PROJECT_MGMT CM ON POM.CONTRACT_MGMT_OBJID = CM.OBJID
         LEFT JOIN CLIENT_MNG   C  ON C.OBJID = POM.PARTNER_OBJID
        WHERE POM.OBJID = $1
        LIMIT 1`,
      [pomObjid],
    );
    if (m.rows.length === 0) return null;
    const master = m.rows[0];

    const p = await pool.query(
      `SELECT POP.OBJID                AS order_part_objid,
              POP.PART_OBJID           AS part_objid,
              POP.PART_NO              AS part_no,
              POP.PART_NAME            AS part_name,
              POP.SPEC                 AS spec,
              ''                       AS maker,
              (SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = POP.UNIT) AS unit_title,
              COALESCE(NULLIF(POP.ORDER_QTY,'')::numeric, 0)             AS order_qty,
              COALESCE(POP.DELIVERY_REQUEST_DATE, '')                     AS delivery_request_date,
              COALESCE((SELECT SUM(COALESCE(NULLIF(AP.RECEIPT_QTY,'')::numeric, 0))
                          FROM ARRIVAL_PLAN AP
                         WHERE AP.ORDER_PART_OBJID = POP.OBJID), 0)       AS arrival_qty
         FROM PURCHASE_ORDER_PART POP
        WHERE POP.PURCHASE_ORDER_MASTER_OBJID = $1
        ORDER BY POP.REGDATE`,
      [pomObjid],
    );

    const parts = p.rows.map((r) => {
      const orderQty = Number(r.order_qty || 0);
      const arrivalQty = Number(r.arrival_qty || 0);
      return {
        order_part_objid: String(r.order_part_objid ?? ""),
        part_objid: String(r.part_objid ?? ""),
        part_no: String(r.part_no ?? ""),
        part_name: String(r.part_name ?? ""),
        spec: String(r.spec ?? ""),
        maker: String(r.maker ?? ""),
        unit_title: String(r.unit_title ?? ""),
        order_qty: orderQty,
        arrival_qty: arrivalQty,
        non_arrival_qty: Math.max(orderQty - arrivalQty, 0),
        delivery_request_date: String(r.delivery_request_date ?? ""),
      };
    });

    const a = await pool.query(
      `SELECT OBJID, ORDER_PART_OBJID, PART_OBJID::VARCHAR, GROUP_SEQ, SEQ,
              RECEIPT_DATE, LOCATION, SUB_LOCATION,
              COALESCE(NULLIF(RECEIPT_QTY,'')::numeric, 0) AS receipt_qty,
              COALESCE(NULLIF(ARRIVAL_QTY,'')::numeric, 0) AS arrival_qty,
              COALESCE(INVENTORY_STATUS, '')               AS inventory_status
         FROM ARRIVAL_PLAN
        WHERE PARENT_OBJID = $1
        ORDER BY GROUP_SEQ, SEQ`,
      [pomObjid],
    );

    const arrivals = a.rows.map((r) => ({
      objid: String(r.objid ?? ""),
      order_part_objid: String(r.order_part_objid ?? ""),
      part_objid: String(r.part_objid ?? ""),
      group_seq: String(r.group_seq ?? "1"),
      seq: String(r.seq ?? ""),
      receipt_date: String(r.receipt_date ?? ""),
      location: String(r.location ?? ""),
      sub_location: String(r.sub_location ?? ""),
      receipt_qty: Number(r.receipt_qty || 0),
      arrival_qty: Number(r.arrival_qty || 0),
      inventory_status: String(r.inventory_status ?? ""),
    }));

    return {
      master: {
        pom_objid: String(master.pom_objid ?? ""),
        purchase_order_no: String(master.purchase_order_no ?? ""),
        project_no: String(master.project_no ?? ""),
        contract_mgmt_objid: String(master.contract_mgmt_objid ?? ""),
        partner_objid: String(master.partner_objid ?? ""),
        partner_name: String(master.partner_name ?? ""),
        delivery_status: String(master.delivery_status ?? ""),
      },
      parts,
      arrivals,
    };
  } catch (e: any) {
    logger.error("getInboundFormInit 실패", { error: e.message, pomObjid });
    throw e;
  }
}

/** POST /api/purchase/inbound-form/save — arrival_plan 다수 UPSERT 트랜잭션 */
export async function saveInboundForm(
  pomObjid: string,
  rows: InboundSaveRow[],
  writer: string,
): Promise<{ saved: number }> {
  if (!pomObjid) throw new Error("pomObjid is required");
  if (!Array.isArray(rows)) throw new Error("rows must be array");

  const pool = getPool();
  const client = await pool.connect();
  try {
    await client.query("BEGIN");
    let saved = 0;
    for (const row of rows) {
      const qty = Number(row.receipt_qty || 0);
      // 입고수량 0 인 행은 저장 skip
      if (qty <= 0) continue;

      const objid = row.objid || createObjId();
      // ARRIVAL_PLAN UPSERT (wace supplyChainMgmt.saveDeliveryInfo 1:1)
      await client.query(
        `INSERT INTO ARRIVAL_PLAN
           (OBJID, PARENT_OBJID, ORDER_PART_OBJID, PART_OBJID,
            RECEIPT_QTY, RECEIPT_DATE, LOCATION, SUB_LOCATION,
            WRITER, RECEIVER_ID, GROUP_SEQ, SEQ, ARRIVAL_QTY, ARRIVAL_PLAN_DATE)
         VALUES ($1, $2, $3, $4::bigint,
                 $5, $6, $7, $8,
                 $9, $9, $10, $11, $12, $13)
         ON CONFLICT (OBJID) DO UPDATE SET
                 RECEIPT_QTY       = EXCLUDED.RECEIPT_QTY,
                 RECEIPT_DATE      = EXCLUDED.RECEIPT_DATE,
                 LOCATION          = EXCLUDED.LOCATION,
                 SUB_LOCATION      = EXCLUDED.SUB_LOCATION,
                 ARRIVAL_QTY       = EXCLUDED.ARRIVAL_QTY,
                 ARRIVAL_PLAN_DATE = EXCLUDED.ARRIVAL_PLAN_DATE,
                 RECEIVER_ID       = EXCLUDED.RECEIVER_ID`,
        [
          objid,
          pomObjid,
          row.order_part_objid,
          row.part_objid || null,
          String(qty),
          row.receipt_date || "",
          row.location || "",
          row.sub_location || "",
          writer || "",
          row.group_seq || "1",
          row.seq || "",
          String(row.arrival_qty || qty),
          row.arrival_plan_date || "",
        ],
      );
      saved++;
    }
    await client.query("COMMIT");
    return { saved };
  } catch (e: any) {
    await client.query("ROLLBACK");
    logger.error("saveInboundForm 실패", { error: e.message, pomObjid });
    throw e;
  } finally {
    client.release();
  }
}

/** POST /api/purchase/arrival/deadline — 마감정보 일괄 UPDATE */
export async function saveDeadlineInfo(body: DeadlineInfoBody): Promise<{ updated: number }> {
  if (!Array.isArray(body.objIds) || body.objIds.length === 0) {
    throw new Error("objIds required");
  }
  const pool = getPool();

  // wace 패턴: tax_type 은 항상 SET, 나머지는 비어있지 않을 때만
  const sets: string[] = [`tax_type = $1`];
  const vals: any[] = [body.taxType ?? ""];
  let i = 2;
  const addIf = (col: string, v?: string) => {
    if (v != null && v !== "") {
      sets.push(`${col} = $${i++}`);
      vals.push(v);
    }
  };
  const addNumIf = (col: string, v?: string) => {
    if (v != null && v !== "") {
      sets.push(`${col} = $${i++}::numeric`);
      vals.push(v);
    }
  };
  addIf("tax_invoice_date", body.taxInvoiceDate);
  if (body.exportDeclNo != null) { sets.push(`export_decl_no = $${i++}`); vals.push(body.exportDeclNo); }
  addIf("loading_date", body.loadingDate);
  addIf("foreign_type", body.foreignType);
  addNumIf("duty", body.duty);
  addNumIf("exchange_rate", body.exchangeRate);
  addNumIf("import_vat", body.importVat);

  const idIdx = i;
  vals.push(body.objIds);

  const sql = `UPDATE arrival_plan
                  SET ${sets.join(", ")}
                WHERE OBJID = ANY($${idIdx}::text[])`;
  try {
    const r = await pool.query(sql, vals);
    return { updated: r.rowCount ?? 0 };
  } catch (e: any) {
    logger.error("saveDeadlineInfo 실패", { error: e.message });
    throw e;
  }
}

/** 입고창고 옵션 — RPS warehouse_info 기반 (wace 는 WAREHOUSE_LOCATION) */
export async function listWarehouseOptions(): Promise<{ code: string; label: string }[]> {
  const pool = getPool();
  try {
    const r = await pool.query(
      `SELECT WAREHOUSE_CODE                                              AS code,
              WAREHOUSE_CODE || ' ' || COALESCE(WAREHOUSE_NAME, '')        AS label
         FROM WAREHOUSE_INFO
        WHERE COALESCE(STATUS, 'active') NOT IN ('inactive', 'delete', 'D', 'N')
          AND WAREHOUSE_CODE IS NOT NULL AND WAREHOUSE_CODE <> ''
        ORDER BY WAREHOUSE_CODE`,
    );
    return r.rows;
  } catch (e: any) {
    logger.error("listWarehouseOptions 실패", { error: e.message });
    return [];
  }
}

/** 계정과목 옵션 — RPS account_code_info 기반 (wace 는 ERP_ACCT_CODE) */
export async function listAcctCodeOptions(): Promise<{ code: string; label: string }[]> {
  const pool = getPool();
  try {
    const r = await pool.query(
      `SELECT ACCOUNT_CODE                                                AS code,
              ACCOUNT_CODE || ' ' || COALESCE(ACCOUNT_NAME, '')             AS label
         FROM ACCOUNT_CODE_INFO
        WHERE COALESCE(USE_YN, 'Y') IN ('Y', 'y', '1')
          AND ACCOUNT_CODE IS NOT NULL AND ACCOUNT_CODE <> ''
        ORDER BY ACCOUNT_CODE`,
    );
    return r.rows;
  } catch (e: any) {
    logger.error("listAcctCodeOptions 실패", { error: e.message });
    return [];
  }
}

/** POST /api/purchase/arrival/close — 매입마감 일괄 (PURCHASE_CLOSE_DATE = 오늘) */
export async function closeArrival(objIds: string[]): Promise<{ updated: number }> {
  if (!Array.isArray(objIds) || objIds.length === 0) {
    throw new Error("objIds required");
  }
  const pool = getPool();
  try {
    // 이미 마감된 건 차단 (wace fn_purchaseClose 와 동일)
    const check = await pool.query(
      `SELECT OBJID FROM ARRIVAL_PLAN
        WHERE OBJID = ANY($1::text[])
          AND COALESCE(PURCHASE_CLOSE_DATE, '') <> ''`,
      [objIds],
    );
    if (check.rows.length > 0) {
      const dup = check.rows.map((r: any) => r.objid).join(", ");
      throw new Error(`이미 매입마감된 건이 포함돼 있어요 (${dup})`);
    }
    const r = await pool.query(
      `UPDATE ARRIVAL_PLAN
          SET PURCHASE_CLOSE_DATE = TO_CHAR(NOW(), 'YYYY-MM-DD')
        WHERE OBJID = ANY($1::text[])`,
      [objIds],
    );
    return { updated: r.rowCount ?? 0 };
  } catch (e: any) {
    logger.error("closeArrival 실패", { error: e.message });
    throw e;
  }
}
