/**
 * 출고관리 컨트롤러
 *
 * 출고유형별 소스 테이블:
 *  - 판매출고 → shipment_instruction + shipment_instruction_detail (출하지시)
 *  - 반품출고 → purchase_order_mng (발주/입고)
 *  - 기타출고 → item_info (품목)
 */

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

// 출고 목록 조회
export async function getList(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const {
      outbound_type,
      outbound_status,
      search_keyword,
      date_from,
      date_to,
    } = req.query;

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

    if (companyCode === "*") {
      // 최고 관리자: 전체 조회
    } else {
      conditions.push(`om.company_code = $${paramIdx}`);
      params.push(companyCode);
      paramIdx++;
    }

    if (outbound_type && outbound_type !== "all") {
      conditions.push(`om.outbound_type = $${paramIdx}`);
      params.push(outbound_type);
      paramIdx++;
    }

    if (outbound_status && outbound_status !== "all") {
      conditions.push(`om.outbound_status = $${paramIdx}`);
      params.push(outbound_status);
      paramIdx++;
    }

    if (search_keyword) {
      conditions.push(
        `(om.outbound_number ILIKE $${paramIdx} OR om.item_name ILIKE $${paramIdx} OR om.item_code ILIKE $${paramIdx} OR om.customer_name ILIKE $${paramIdx} OR om.reference_number ILIKE $${paramIdx})`
      );
      params.push(`%${search_keyword}%`);
      paramIdx++;
    }

    if (date_from) {
      conditions.push(`om.outbound_date >= $${paramIdx}`);
      params.push(date_from);
      paramIdx++;
    }
    if (date_to) {
      conditions.push(`om.outbound_date <= $${paramIdx}`);
      params.push(date_to);
      paramIdx++;
    }

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

    const query = `
      SELECT
        om.*,
        wh.warehouse_name
      FROM outbound_mng om
      LEFT JOIN warehouse_info wh
        ON om.warehouse_code = wh.warehouse_code
        AND om.company_code = wh.company_code
      ${whereClause}
      ORDER BY om.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 });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// 출고 등록 (다건)
export async function create(req: AuthenticatedRequest, res: Response) {
  const pool = getPool();
  const client = await pool.connect();

  try {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const { items, outbound_number, outbound_date, warehouse_code, location_code, manager_id, memo } = req.body;

    if (!items || !Array.isArray(items) || items.length === 0) {
      return res.status(400).json({ success: false, message: "출고 품목이 없습니다." });
    }

    await client.query("BEGIN");

    const insertedRows: any[] = [];

    for (const item of items) {
      const result = await client.query(
        `INSERT INTO outbound_mng (
          company_code, outbound_number, outbound_type, outbound_date,
          reference_number, customer_code, customer_name,
          item_code, item_name, specification, material, unit,
          outbound_qty, unit_price, total_amount,
          lot_number, warehouse_code, location_code,
          outbound_status, manager_id, memo,
          source_type, sales_order_id, shipment_plan_id, item_info_id,
          destination_code, delivery_destination, delivery_address,
          created_date, created_by, writer, status
        ) VALUES (
          $1, $2, $3, $4,
          $5, $6, $7,
          $8, $9, $10, $11, $12,
          $13, $14, $15,
          $16, $17, $18,
          $19, $20, $21,
          $22, $23, $24, $25,
          $26, $27, $28,
          NOW(), $29, $29, '출고'
        ) RETURNING *`,
        [
          companyCode,
          outbound_number || item.outbound_number,
          item.outbound_type,
          outbound_date || item.outbound_date,
          item.reference_number || null,
          item.customer_code || null,
          item.customer_name || null,
          item.item_code || item.item_number || null,
          item.item_name || null,
          item.spec || item.specification || null,
          item.material || null,
          item.unit || "EA",
          item.outbound_qty || 0,
          item.unit_price || 0,
          item.total_amount || 0,
          item.lot_number || null,
          warehouse_code || item.warehouse_code || null,
          location_code || item.location_code || null,
          item.outbound_status || "대기",
          manager_id || item.manager_id || null,
          memo || item.memo || null,
          item.source_type || null,
          item.sales_order_id || null,
          item.shipment_plan_id || null,
          item.item_info_id || null,
          item.destination_code || null,
          item.delivery_destination || null,
          item.delivery_address || null,
          userId,
        ]
      );

      insertedRows.push(result.rows[0]);

      // 재고 업데이트 (inventory_stock): 출고 수량 차감
      const itemCode = item.item_code || item.item_number || null;
      const whCode = warehouse_code || item.warehouse_code || null;
      const locCode = location_code || item.location_code || null;
      const outQty = Number(item.outbound_qty) || 0;
      if (itemCode && outQty > 0) {
        const existingStock = await client.query(
          `SELECT id FROM inventory_stock
           WHERE company_code = $1 AND item_code = $2
             AND COALESCE(warehouse_code, '') = COALESCE($3, '')
             AND COALESCE(location_code, '') = COALESCE($4, '')
           LIMIT 1`,
          [companyCode, itemCode, whCode || '', locCode || '']
        );

        if (existingStock.rows.length > 0) {
          await client.query(
            `UPDATE inventory_stock
             SET current_qty = CAST(GREATEST(COALESCE(CAST(NULLIF(current_qty, '') AS numeric), 0) - $1, 0) AS text),
                 last_out_date = NOW(),
                 updated_date = NOW()
             WHERE id = $2`,
            [outQty, existingStock.rows[0].id]
          );
        } else {
          // 재고 레코드가 없으면 0으로 생성 (마이너스 방지)
          await client.query(
            `INSERT INTO inventory_stock (
              company_code, item_code, warehouse_code, location_code,
              current_qty, safety_qty, last_out_date,
              created_date, updated_date, writer
            ) VALUES ($1, $2, $3, $4, '0', '0', NOW(), NOW(), NOW(), $5)`,
            [companyCode, itemCode, whCode, locCode, userId]
          );
        }
      }

      // 판매출고인 경우 출하지시의 ship_qty 업데이트
      if (item.outbound_type === "판매출고" && item.source_id && item.source_type === "shipment_instruction_detail") {
        await client.query(
          `UPDATE shipment_instruction_detail
           SET ship_qty = COALESCE(ship_qty, 0) + $1,
               updated_date = NOW()
           WHERE id = $2 AND company_code = $3`,
          [item.outbound_qty || 0, item.source_id, companyCode]
        );
      }
    }

    await client.query("COMMIT");

    logger.info("출고 등록 완료", {
      companyCode,
      userId,
      count: insertedRows.length,
      outbound_number,
    });

    return res.json({
      success: true,
      data: insertedRows,
      message: `${insertedRows.length}건 출고 등록 완료`,
    });
  } catch (error: any) {
    await client.query("ROLLBACK");
    logger.error("출고 등록 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  } finally {
    client.release();
  }
}

// 출고 수정
export async function update(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const { id } = req.params;
    const {
      outbound_date, outbound_qty, unit_price, total_amount,
      lot_number, warehouse_code, location_code,
      outbound_status, manager_id: mgr, memo,
    } = req.body;

    const pool = getPool();
    const result = await pool.query(
      `UPDATE outbound_mng SET
        outbound_date = COALESCE($1, outbound_date),
        outbound_qty = COALESCE($2, outbound_qty),
        unit_price = COALESCE($3, unit_price),
        total_amount = COALESCE($4, total_amount),
        lot_number = COALESCE($5, lot_number),
        warehouse_code = COALESCE($6, warehouse_code),
        location_code = COALESCE($7, location_code),
        outbound_status = COALESCE($8, outbound_status),
        manager_id = COALESCE($9, manager_id),
        memo = COALESCE($10, memo),
        updated_date = NOW(),
        updated_by = $11
      WHERE id = $12 AND company_code = $13
      RETURNING *`,
      [
        outbound_date, outbound_qty, unit_price, total_amount,
        lot_number, warehouse_code, location_code,
        outbound_status, mgr, memo,
        userId, id, companyCode,
      ]
    );

    if (result.rowCount === 0) {
      return res.status(404).json({ success: false, message: "출고 데이터를 찾을 수 없습니다." });
    }

    logger.info("출고 수정", { companyCode, userId, id });

    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 deleteOutbound(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { id } = req.params;
    const pool = getPool();

    const result = await pool.query(
      `DELETE FROM outbound_mng WHERE id = $1 AND company_code = $2 RETURNING id`,
      [id, companyCode]
    );

    if (result.rowCount === 0) {
      return res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다." });
    }

    logger.info("출고 삭제", { companyCode, id });

    return res.json({ success: true, message: "삭제 완료" });
  } catch (error: any) {
    logger.error("출고 삭제 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// 판매출고용: 출하지시 데이터 조회
export async function getShipmentInstructions(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { keyword } = req.query;

    const conditions: string[] = ["si.company_code = $1"];
    const params: any[] = [companyCode];
    let paramIdx = 2;

    if (keyword) {
      conditions.push(
        `(si.instruction_no ILIKE $${paramIdx} OR sid.item_name ILIKE $${paramIdx} OR sid.item_code ILIKE $${paramIdx})`
      );
      params.push(`%${keyword}%`);
      paramIdx++;
    }

    const pool = getPool();
    const result = await pool.query(
      `SELECT
        sid.id AS detail_id,
        si.id AS instruction_id,
        si.instruction_no,
        si.instruction_date,
        si.partner_id,
        si.status AS instruction_status,
        sid.item_code,
        sid.item_name,
        sid.spec,
        sid.material,
        COALESCE(sid.plan_qty, 0) AS plan_qty,
        COALESCE(sid.ship_qty, 0) AS ship_qty,
        COALESCE(sid.order_qty, 0) AS order_qty,
        GREATEST(COALESCE(sid.plan_qty, 0) - COALESCE(sid.ship_qty, 0), 0) AS remain_qty,
        sid.source_type
      FROM shipment_instruction si
      JOIN shipment_instruction_detail sid
        ON si.id = sid.instruction_id
        AND si.company_code = sid.company_code
      WHERE ${conditions.join(" AND ")}
        AND COALESCE(sid.plan_qty, 0) > COALESCE(sid.ship_qty, 0)
      ORDER BY si.instruction_date DESC, si.instruction_no`,
      params
    );

    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 getPurchaseOrders(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { keyword } = req.query;

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

    // 입고된 것만 (반품 대상)
    conditions.push(
      `COALESCE(CAST(NULLIF(received_qty, '') AS numeric), 0) > 0`
    );

    if (keyword) {
      conditions.push(
        `(purchase_no ILIKE $${paramIdx} OR item_name ILIKE $${paramIdx} OR item_code ILIKE $${paramIdx} OR supplier_name ILIKE $${paramIdx})`
      );
      params.push(`%${keyword}%`);
      paramIdx++;
    }

    const pool = getPool();
    const result = await pool.query(
      `SELECT
        id, purchase_no, order_date, supplier_code, supplier_name,
        item_code, item_name, spec, material,
        COALESCE(CAST(NULLIF(order_qty, '') AS numeric), 0) AS order_qty,
        COALESCE(CAST(NULLIF(received_qty, '') AS numeric), 0) AS received_qty,
        COALESCE(CAST(NULLIF(unit_price, '') AS numeric), 0) AS unit_price,
        status, due_date
      FROM purchase_order_mng
      WHERE ${conditions.join(" AND ")}
      ORDER BY order_date DESC, purchase_no`,
      params
    );

    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 getItems(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const { keyword } = req.query;

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

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

    const pool = getPool();
    const result = await pool.query(
      `SELECT
        id, item_number, item_name, size AS spec, material, unit,
        COALESCE(CAST(NULLIF(standard_price, '') AS numeric), 0) AS standard_price
      FROM item_info
      WHERE ${conditions.join(" AND ")}
      ORDER BY item_name`,
      params
    );

    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 generateNumber(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const pool = getPool();

    const today = new Date();
    const yyyy = today.getFullYear();
    const prefix = `OUT-${yyyy}-`;

    const result = await pool.query(
      `SELECT outbound_number FROM outbound_mng
       WHERE company_code = $1 AND outbound_number LIKE $2
       ORDER BY outbound_number DESC LIMIT 1`,
      [companyCode, `${prefix}%`]
    );

    let seq = 1;
    if (result.rows.length > 0) {
      const lastNo = result.rows[0].outbound_number;
      const lastSeq = parseInt(lastNo.replace(prefix, ""), 10);
      if (!isNaN(lastSeq)) seq = lastSeq + 1;
    }

    const newNumber = `${prefix}${String(seq).padStart(4, "0")}`;

    return res.json({ success: true, data: newNumber });
  } catch (error: any) {
    logger.error("출고번호 생성 실패", { error: error.message });
    return res.status(500).json({ success: false, message: error.message });
  }
}

// 창고 목록 조회
export async function getWarehouses(req: AuthenticatedRequest, res: Response) {
  try {
    const companyCode = req.user!.companyCode;
    const pool = getPool();

    const result = await pool.query(
      `SELECT warehouse_code, warehouse_name, warehouse_type
       FROM warehouse_info
       WHERE company_code = $1 AND status != '삭제'
       ORDER BY warehouse_name`,
      [companyCode]
    );

    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 });
  }
}
