import { NextRequest, NextResponse } from "next/server";
import type { PoolClient } from "pg";
import { pool } from "@/lib/db";
import { getSession } from "@/lib/session";
import { createObjectId } from "@/lib/utils";

// 입고결과 저장 (원본: purchaseOrder/saveDeliveryInfo.do → supplyChainMgmt.saveDeliveryInfo)
// 처리 순서:
//  1) 파트×차수 행마다 ARRIVAL_PLAN UPSERT
//  2) RECEIPT_QTY > RECEIPT_INV_QTY(기입고) 인 경우에만 inventory 반영
//     - ARRIVAL_PLAN.INVENTORY_STATUS='Y' 로 마킹
//     - INVENTORY_MGMT(contract_objid, unit, part_objid) UPSERT
//     - 단일발주: INVENTORY_MGMT_IN 1행
//     - 동시발주(MULTI_YN='Y'): 마스터부터 슬레이브 순으로 프로젝트별 ORDER_QTY 만큼 분배
//
// Request body 형식:
// {
//   ORDER_OBJID / PARENT_OBJID : 발주 마스터 objId (필수)
//   TYPE : POM.type
//   MULTI_YN : 'Y' | 'N'
//   CONTRACT_MGMT_OBJID : 프로젝트 objId
//   UNIT_CODE : 유닛
//   // 파트 정보 (DETAIL_GROUP 번호로 그룹핑)
//   parts: [{ ORDER_PART_OBJID, PART_OBJID, LD_PART_OBJID, ORDER_QTY, REAL_ORDER_QTY }]
//   // 차수별 입고 행들 (파트 수 × 차수 수)
//   items: [{
//     OBJID?, GROUP_SEQ, SEQ, DETAIL_GROUP,  // DETAIL_GROUP = 파트 index (1-based)
//     ARRIVAL_QTY, ARRIVAL_PLAN_DATE,
//     RECEIPT_QTY, RECEIPT_INV_QTY, RECEIPT_DATE,
//     LOCATION, SUB_LOCATION,
//     INVENTORY_STATUS,
//   }]
// }
export async function POST(request: NextRequest) {
  const user = await getSession();
  if (!user) return NextResponse.json({ success: false }, { status: 401 });

  const body = await request.json();

  // 구(舊) 호환 (단건 입고): body.items 없을 때 body.objId 단건 처리
  const legacyItems: Record<string, unknown>[] = Array.isArray(body.items) ? body.items : [];
  if (legacyItems.length === 0 && !body.parts && body.objId) {
    return legacySingleSave(body, user.userId);
  }

  const orderObjId: string = String(body.ORDER_OBJID ?? body.PARENT_OBJID ?? body.objId ?? "");
  const parentObjId: string = String(body.PARENT_OBJID ?? body.ORDER_OBJID ?? orderObjId);
  const contractMgmtObjId: string = String(body.CONTRACT_MGMT_OBJID ?? "");
  const unitCode: string = String(body.UNIT_CODE ?? "");
  const multiYn: string = String(body.MULTI_YN ?? "");
  const parts: Record<string, unknown>[] = Array.isArray(body.parts) ? body.parts : [];
  const items: Record<string, unknown>[] = Array.isArray(body.items) ? body.items : [];

  if (!orderObjId) {
    return NextResponse.json(
      { success: false, message: "발주 objId가 없습니다." },
      { status: 400 },
    );
  }
  if (items.length === 0) {
    return NextResponse.json(
      { success: false, message: "저장할 입고 데이터가 없습니다." },
      { status: 400 },
    );
  }

  const partByIdx = new Map<number, Record<string, unknown>>();
  parts.forEach((p, i) => partByIdx.set(i + 1, p));

  const client = await pool.connect();
  try {
    await client.query("BEGIN");

    for (const item of items) {
      const arrivalObjId = String(item.OBJID ?? "") || createObjectId();
      const detailGroup = Number(item.DETAIL_GROUP ?? 0) || 1;
      const part = partByIdx.get(detailGroup);
      const orderPartObjId = String(part?.ORDER_PART_OBJID ?? "");
      const partObjId = String(part?.PART_OBJID ?? "");
      const ldPartObjId = String(part?.LD_PART_OBJID ?? "");
      const realOrderQty = Number(part?.REAL_ORDER_QTY ?? 0) || 0;
      const orderQty = Number(part?.ORDER_QTY ?? realOrderQty) || realOrderQty;

      const receiptQty = Number(item.RECEIPT_QTY ?? 0) || 0;
      const receiptInvQty = Number(item.RECEIPT_INV_QTY ?? 0) || 0;
      const arrivalQty = Number(item.ARRIVAL_QTY ?? 0) || 0;
      const receiptDate = String(item.RECEIPT_DATE ?? "");
      const arrivalPlanDate = String(item.ARRIVAL_PLAN_DATE ?? "");
      const location = String(item.LOCATION ?? "");
      const subLocation = String(item.SUB_LOCATION ?? "");
      const groupSeq = String(item.GROUP_SEQ ?? "1");
      const seq = String(item.SEQ ?? "1");

      // 1) ARRIVAL_PLAN UPSERT
      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,$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`,
        [
          arrivalObjId, parentObjId, orderPartObjId, partObjId,
          String(receiptQty), receiptDate, location, subLocation,
          user.userId, groupSeq, seq, String(arrivalQty), arrivalPlanDate,
        ],
      );

      // 2) 실제 신규 입고가 없으면(receiptQty=0 또는 기입고와 동일) 재고 반영 skip
      const deltaQty = receiptQty - receiptInvQty;
      if (receiptQty <= 0 || deltaQty <= 0) continue;

      // INVENTORY_STATUS='Y'
      await client.query(
        `UPDATE arrival_plan SET inventory_status = 'Y', receiver_id = $1 WHERE objid = $2`,
        [user.userId, arrivalObjId],
      );

      // 3) 재고 반영 — 마스터(본인 프로젝트)부터 처리
      let remainingQty = deltaQty;

      // 마스터 inventory_mgmt 행 확보(없으면 생성)
      const masterInvParent = await ensureInventoryMgmt(client, {
        contractObjId: contractMgmtObjId,
        unit: unitCode,
        partObjId,
        orderObjId,
        ldPartObjId,
        receiptQty: String(deltaQty),
        location,
        subLocation,
        writer: user.userId,
      });

      if (multiYn !== "Y") {
        // 단일발주: 마스터에 전부 기록
        await insertInventoryIn(client, {
          parentObjId: masterInvParent,
          receiptQty: String(deltaQty),
          location, subLocation,
          writer: user.userId,
          contractMgmtObjId,
          poMasterObjId: orderObjId,
          poSubObjId: orderObjId,
        });
        continue;
      }

      // 동시발주: 마스터부터 min(ORDER_QTY - 기입고, remaining) 분배
      // 마스터 기존 입고수량 (동일 purchase_order_master_objid + contract + parent)
      const masterExistingQty = await getExistingInventoryInQty(client, {
        poMasterObjId: orderObjId,
        contractMgmtObjId,
        parentObjId: masterInvParent,
      });
      const masterInsertable = Math.max(0, orderQty - masterExistingQty);
      const masterInsert = Math.min(masterInsertable, remainingQty);
      if (masterInsert > 0) {
        await insertInventoryIn(client, {
          parentObjId: masterInvParent,
          receiptQty: String(masterInsert),
          location, subLocation,
          writer: user.userId,
          contractMgmtObjId,
          poMasterObjId: orderObjId,
          poSubObjId: orderObjId,
        });
        remainingQty -= masterInsert;
      }

      if (remainingQty <= 0) continue;

      // 슬레이브들 순차 분배
      const slaves = await client.query(
        `SELECT objid::text AS objid,
                contract_mgmt_objid::text AS contract_mgmt_objid,
                unit_code
           FROM purchase_order_master
          WHERE multi_master_objid = $1
          ORDER BY (SELECT project_no FROM project_mgmt CNT WHERE CNT.objid = purchase_order_master.contract_mgmt_objid LIMIT 1)`,
        [orderObjId],
      );

      for (const slave of slaves.rows as Array<{ objid: string; contract_mgmt_objid: string; unit_code: string }>) {
        if (remainingQty <= 0) break;

        const slaveContractObjId = slave.contract_mgmt_objid ?? "";
        const slaveUnit = slave.unit_code ?? "";
        const slavePoObjId = slave.objid;

        // 슬레이브 inventory_mgmt 확보
        const slaveInvParent = await ensureInventoryMgmt(client, {
          contractObjId: slaveContractObjId,
          unit: slaveUnit,
          partObjId,
          orderObjId,
          ldPartObjId,
          receiptQty: String(remainingQty),
          location,
          subLocation,
          writer: user.userId,
        });

        const slaveExistingQty = await getExistingInventoryInQty(client, {
          poMasterObjId: orderObjId,
          contractMgmtObjId: slaveContractObjId,
          parentObjId: slaveInvParent,
        });
        const slaveInsertable = Math.max(0, orderQty - slaveExistingQty);
        const slaveInsert = Math.min(slaveInsertable, remainingQty);
        if (slaveInsert <= 0) continue;

        await insertInventoryIn(client, {
          parentObjId: slaveInvParent,
          receiptQty: String(slaveInsert),
          location, subLocation,
          writer: user.userId,
          contractMgmtObjId: slaveContractObjId,
          poMasterObjId: orderObjId,
          poSubObjId: slavePoObjId,
        });
        remainingQty -= slaveInsert;
      }
    }

    await client.query("COMMIT");
    return NextResponse.json({ success: true, message: "저장되었습니다." });
  } catch (e) {
    await client.query("ROLLBACK");
    console.error("delivery/acceptance/save:", e);
    return NextResponse.json(
      { success: false, message: "저장 중 오류가 발생했습니다." },
      { status: 500 },
    );
  } finally {
    client.release();
  }
}

async function ensureInventoryMgmt(
  client: PoolClient,
  p: {
    contractObjId: string; unit: string; partObjId: string;
    orderObjId: string; ldPartObjId: string;
    receiptQty: string; location: string; subLocation: string; writer: string;
  },
): Promise<string> {
  if (!p.partObjId) return "";
  const found = await client.query(
    `SELECT objid::text AS objid FROM inventory_mgmt
      WHERE contract_objid = $1 AND unit = $2 AND part_objid = $3 LIMIT 1`,
    [p.contractObjId, p.unit, p.partObjId],
  );
  if (found.rows[0]?.objid) return String(found.rows[0].objid);

  const newObjId = createObjectId();
  await client.query(
    `INSERT INTO inventory_mgmt (
       objid, contract_objid, unit, part_objid,
       cls_cd, qty, location, sub_location,
       reg_date, price, writer
     )
     VALUES ($1,$2,$3,$4,'0001205',$5,$6,$7,
       TO_CHAR(NOW(),'YYYY-MM-DD'),
       (SELECT partner_price FROM purchase_order_part
          WHERE purchase_order_master_objid = $8 AND part_objid = $4 LIMIT 1),
       $9)
     ON CONFLICT (contract_objid, unit, part_objid) DO NOTHING`,
    [
      newObjId, p.contractObjId, p.unit, p.partObjId,
      p.receiptQty, p.location, p.subLocation,
      p.orderObjId, p.writer,
    ],
  );
  // 경합 시 기존 값 재조회
  const again = await client.query(
    `SELECT objid::text AS objid FROM inventory_mgmt
      WHERE contract_objid = $1 AND unit = $2 AND part_objid = $3 LIMIT 1`,
    [p.contractObjId, p.unit, p.partObjId],
  );
  return String(again.rows[0]?.objid ?? newObjId);
}

async function getExistingInventoryInQty(
  client: PoolClient,
  p: { poMasterObjId: string; contractMgmtObjId: string; parentObjId: string },
): Promise<number> {
  const r = await client.query(
    `SELECT SUM(CASE WHEN receipt_qty IS NULL OR receipt_qty = '' THEN 0 ELSE receipt_qty::numeric END) AS qty
       FROM inventory_mgmt_in
      WHERE purchase_order_master_objid = $1
        AND contract_mgmt_objid = $2
        AND parent_objid = $3`,
    [p.poMasterObjId, p.contractMgmtObjId, p.parentObjId],
  );
  return Number(r.rows[0]?.qty ?? 0) || 0;
}

async function insertInventoryIn(
  client: PoolClient,
  p: {
    parentObjId: string; receiptQty: string;
    location: string; subLocation: string; writer: string;
    contractMgmtObjId: string; poMasterObjId: string; poSubObjId: string;
  },
) {
  await client.query(
    `INSERT INTO inventory_mgmt_in (
       objid, parent_objid, receipt_qty, location, sub_location,
       writer, regdate, contract_mgmt_objid,
       purchase_order_master_objid, purchase_order_sub_objid
     ) VALUES ($1,$2,$3,$4,$5,$6,NOW(),$7,$8,$9)`,
    [
      createObjectId(), p.parentObjId, p.receiptQty, p.location, p.subLocation,
      p.writer, p.contractMgmtObjId, p.poMasterObjId, p.poSubObjId,
    ],
  );
}

// 구(舊) 단건 호환 — 기존 arrival-plan 팝업이 호출하는 경로 유지
async function legacySingleSave(body: Record<string, unknown>, writer: string) {
  const objId = String(body.objId ?? "") || createObjectId();
  const parentObjId = String(body.purchase_order_master_objid ?? body.parent_objid ?? "");
  const orderPartObjId = String(body.purchase_order_part_objid ?? body.order_part_objid ?? "");
  const partObjId = String(body.part_objid ?? "");
  const receiptQty = String(body.receipt_qty ?? "0");
  const receiptDate = String(body.receipt_date ?? "");
  const arrivalQty = String(body.arrival_qty ?? receiptQty);
  const arrivalPlanDate = String(body.arrival_plan_date ?? receiptDate);
  const location = String(body.location ?? "");
  const subLocation = String(body.sub_location ?? "");
  const groupSeq = String(body.group_seq ?? "1");
  const seq = String(body.seq ?? "1");

  const client = await pool.connect();
  try {
    await client.query("BEGIN");
    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,$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, parentObjId, orderPartObjId, partObjId,
        receiptQty, receiptDate, location, subLocation,
        writer, groupSeq, seq, arrivalQty, arrivalPlanDate,
      ],
    );
    await client.query("COMMIT");
    return NextResponse.json({ success: true, message: "저장되었습니다." });
  } catch (e) {
    await client.query("ROLLBACK");
    console.error("delivery/acceptance/save legacy:", e);
    return NextResponse.json(
      { success: false, message: "저장 중 오류가 발생했습니다." },
      { status: 500 },
    );
  } finally {
    client.release();
  }
}
