"use client";

import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
  Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
} from "@/components/ui/select";
import {
  Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle,
} from "@/components/ui/dialog";
import { Plus, Save, Trash2, Loader2, Search, Pencil, Send } from "lucide-react";
import { toast } from "sonner";
import { useAuth } from "@/hooks/useAuth";
import { useConfirmDialog } from "@/components/common/ConfirmDialog";
import { DataGrid, DataGridColumn } from "@/components/common/DataGrid";
import { CustomerSelect } from "@/components/common/CustomerSelect";
import { PartSelect } from "@/components/common/PartSelect";
import { CommCodeSelect } from "@/components/common/CommCodeSelect";
import { SmartSelect, SmartSelectOption } from "@/components/common/SmartSelect";
import { PageHeader } from "@/components/common/PageHeader";
import { CompactFilterBar, CompactFilterField, CompactDateRange } from "@/components/common/CompactFilterBar";
import { ItemSearchDialog, ItemRow } from "@/components/common/ItemSearchDialog";
import { AttachmentDialog } from "@/components/common/AttachmentDialog";
import { EstimateMailDialog } from "@/components/sales/EstimateMailDialog";
import { salesEstimateApi, EstimateRow, EstimateBody, EstimateItem } from "@/lib/api/salesEstimate";
import { exportToExcel } from "@/lib/utils/excelExport";

// ─── 컬럼 ─────────────────────────────────────────────────────

// wace_plm 원본 견적관리 그리드 컬럼 순서를 그대로 따름
const GRID_COLUMNS: DataGridColumn[] = [
  { key: "contract_no", label: "영업번호", width: "w-[125px]", frozen: true },
  { key: "category_name", label: "주문유형", width: "w-[115px]", align: "center" },
  { key: "receipt_date", label: "접수일", width: "w-[115px]", align: "center" },
  { key: "earliest_due_date_label", label: "요청납기", width: "w-[160px]", align: "center" },
  { key: "customer_name", label: "고객사", width: "w-[150px]" },
  { key: "item_summary", label: "품명", width: "w-[200px]" },
  { key: "estimate_quantity", label: "견적수량", width: "w-[115px]", align: "right", formatNumber: true },
  { key: "paid_type_name", label: "유/무상", width: "w-[100px]", align: "center" },
  { key: "est_total_amount", label: "공급가액", width: "w-[130px]", formatMoney: true },
  { key: "est_total_amount_krw", label: "원화환산공급가액", width: "w-[180px]", formatMoney: true },
  { key: "est_status", label: "견적현황", width: "w-[115px]", align: "center", renderType: "folder" },
  { key: "add_est_cnt", label: "추가견적", width: "w-[115px]", align: "center", renderType: "clip" },
  { key: "appr_status", label: "결재상태", width: "w-[115px]", align: "center" },
  { key: "mail_send_status_label", label: "메일발송", width: "w-[125px]", align: "center" },
  { key: "contract_currency_name", label: "환종", width: "w-[95px]", align: "center" },
  { key: "exchange_rate", label: "환율", width: "w-[95px]", formatMoney: true },
  { key: "serial_no", label: "S/N", width: "w-[140px]" },
  { key: "part_no", label: "품번", width: "w-[120px]" },
  { key: "writer_name", label: "작성자", width: "w-[115px]" },
  /* wace estimateList_new.jsp 494~502 — 비활성(주석) 컬럼. 활성화 시 아래 주석 해제.
  { key: "product_name", label: "제품구분", width: "w-[90px]", align: "center" },
  { key: "area_name", label: "국내/해외", width: "w-[90px]", align: "center" },
  { key: "return_reason_summary", label: "반납사유", width: "w-[120px]" },
  */
];

// ─── 코드 라벨 ────────────────────────────────────────────────

const PAID_TYPES: Record<string, string> = { paid: "유상", free: "무상" };
const PAID_TYPE_OPTIONS = [
  { value: "", label: "전체" }, { value: "paid", label: "유상" }, { value: "free", label: "무상" },
];

// wace estimateRegistFormPopup 기본값
const DEFAULT_FORM: EstimateBody = {
  category_cd: "",
  area_cd: "",
  customer_objid: "",
  paid_type: "paid",
  receipt_date: "",
  contract_currency: "",
  exchange_rate: "",
  approval_required: "N",
  items: [],
};

const EMPTY_ITEM: EstimateItem = {
  seq: 1,
  product: "", part_objid: "", part_no: "", part_name: "",
  serials: [], quantity: "", due_date: "", return_reason: "", customer_request: "",
};

// ─── 페이지 ───────────────────────────────────────────────────

export default function SalesEstimatePage() {
  const { user } = useAuth();
  const { confirm, ConfirmDialogComponent } = useConfirmDialog();

  // 목록 상태
  const [rows, setRows] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);
  const [selected, setSelected] = useState<EstimateRow | null>(null);

  // 검색 폼 — wace 원본 estimateList_new.jsp 활성 7개
  // 주문유형/고객사/품번/품명/S/N/결재상태/접수일
  const [searchForm, setSearchForm] = useState({
    category_cd: "",
    customer_objid: "",
    search_partObjId: "",   // 품번 (PartSelect → part_objid)
    search_partName: "",    // 품명 (PartSelect → part_objid 별도 사용 시 동기화)
    search_serialNo: "",
    appr_status: "",
    receipt_start_date: "",
    receipt_end_date: "",
  });

  // 등록/수정 다이얼로그
  const [dialogOpen, setDialogOpen] = useState(false);
  const [dialogMode, setDialogMode] = useState<"create" | "edit">("create");
  const [saving, setSaving] = useState(false);
  const [form, setForm] = useState<EstimateBody>({ ...DEFAULT_FORM });
  const [contractNoDisplay, setContractNoDisplay] = useState<string>("");  // 자동채번 표시용

  // 수주확정/수주FCST 행은 품목 추가/삭제 불가 (project_mgmt에 라인 단위로 묶여있음)
  const isContractConfirmed = useMemo(
    () => dialogMode === "edit" && (selected?.contract_result === "0000964" || selected?.contract_result === "0000968"),
    [dialogMode, selected],
  );

  // 품목 검색 모달 (라인별 진입)
  const [itemDialogOpen, setItemDialogOpen] = useState(false);
  const [itemSearchTargetIdx, setItemSearchTargetIdx] = useState<number | null>(null);

  // S/N 관리 모달 (wace fn_openItemSnPopup)
  const [serialDialogOpen, setSerialDialogOpen] = useState(false);
  const [serialDialogIdx, setSerialDialogIdx] = useState<number | null>(null);
  const [serialDraft, setSerialDraft] = useState<string[]>([]);
  const [serialInput, setSerialInput] = useState("");

  // 연속번호 생성 모달 (wace fn_openItemSequentialSnPopup)
  const [seqDialogOpen, setSeqDialogOpen] = useState(false);
  const [seqStartNo, setSeqStartNo] = useState("");
  const [seqCount, setSeqCount] = useState("");

  // 메일 발송 다이얼로그 — wace estimateMailFormPopup.jsp 1:1
  const [mailDialogOpen, setMailDialogOpen] = useState(false);

  // 첨부파일 다이얼로그 (추가견적 클립 컬럼 클릭 시)
  // G5 견적작성 — 일반/장비 선택 다이얼로그
  const [templateChoiceOpen, setTemplateChoiceOpen] = useState(false);
  // 견적 차수 리스트 다이얼로그 (est_status folder 클릭)
  const [templateListOpen, setTemplateListOpen] = useState(false);
  const [templateList, setTemplateList] = useState<any[]>([]);

  function openTemplateChoice() {
    if (!selected) { toast.warning("견적을 선택하세요."); return; }
    setTemplateChoiceOpen(true);
  }

  function pickTemplate(templateType: "1" | "2") {
    if (!selected) return;
    setTemplateChoiceOpen(false);
    const url = `/COMPANY_16/sales/estimate/template${templateType}/pop/${encodeURIComponent(selected.objid)}`;
    window.open(url, `estimateTemplate_${selected.objid}_${templateType}`,
      "width=1280,height=900,menubar=no,scrollbars=yes,resizable=yes");
  }

  async function openTemplateList(contractObjid: string) {
    try {
      const list = await salesEstimateApi.listTemplates(contractObjid);
      setTemplateList(list);
      setTemplateListOpen(true);
    } catch (e: any) {
      toast.error("견적 차수 리스트 조회 실패: " + (e?.message ?? ""));
    }
  }

  function openExistingTemplate(templateObjid: string, templateType: string) {
    const t = templateType === "2" ? "2" : "1";
    const contractObjid = selected?.objid ?? "";
    const url = `/COMPANY_16/sales/estimate/template${t}/pop/${encodeURIComponent(contractObjid)}?templateObjid=${encodeURIComponent(templateObjid)}`;
    window.open(url, `estimateTemplate_${templateObjid}`,
      "width=1280,height=900,menubar=no,scrollbars=yes,resizable=yes");
    setTemplateListOpen(false);
  }

  const [attachDialogOpen, setAttachDialogOpen] = useState(false);
  const [attachContext, setAttachContext] = useState<{
    targetObjid: string;
    docType: string | string[];
    uploadDocType: string;
    uploadDocTypeName?: string;
    title: string;
  } | null>(null);

  // 클릭 핸들러를 주입한 그리드 컬럼
  const gridColumns = useMemo<DataGridColumn[]>(
    () => GRID_COLUMNS.map((col) => {
      if (col.key === "add_est_cnt") {
        return {
          ...col,
          onClick: (row) => {
            setAttachContext({
              targetObjid: String(row.objid),
              docType: "estimate02",
              uploadDocType: "estimate02",
              uploadDocTypeName: "추가견적",
              title: `추가견적 첨부 — ${row.contract_no ?? ""}`,
            });
            setAttachDialogOpen(true);
          },
        };
      }
      // G5: 견적현황(폴더 아이콘) 클릭 시 차수 리스트 다이얼로그 (wace fn_showEstimateList)
      if (col.key === "est_status") {
        return {
          ...col,
          onClick: (row) => {
            if (!row.est_status || Number(row.est_status) === 0) return;
            setSelected(row as EstimateRow);
            openTemplateList(String(row.objid));
          },
        };
      }
      return col;
    }),
    []
  );

  // ─── 데이터 로드 ────────────────────────────────────────────

  const fetchList = useCallback(async () => {
    if (!user) return;
    setLoading(true);
    try {
      const params: Record<string, string> = {};
      Object.entries(searchForm).forEach(([k, v]) => { if (v) params[k] = v; });
      const data = await salesEstimateApi.list(params);
      const mapped = data.map((r) => ({
        ...r,
        id: r.objid,
        category_name: r.category_name ?? r.category_cd ?? "",
        paid_type_name: r.paid_type_name ?? PAID_TYPES[r.paid_type ?? ""] ?? "",
        earliest_due_date_label: r.other_due_date_count && r.other_due_date_count > 0
          ? `${r.earliest_due_date} 외 ${r.other_due_date_count}건`
          : (r.earliest_due_date ?? ""),
        mail_send_status_label: r.mail_send_status === "Y"
          ? `발송 ${r.mail_send_date ?? ""}`
          : (r.mail_send_status === "N" ? "실패" : ""),
      }));
      setRows(mapped);
    } catch (err: any) {
      toast.error("견적 목록 조회 실패");
    } finally {
      setLoading(false);
    }
  }, [user, searchForm]);

  useEffect(() => { fetchList(); }, [fetchList]);

  // ─── 하단 통계 ──────────────────────────────────────────────
  // 견적 건수 / 견적수량 합계 / 공급가액 합계 / 원화환산공급가액 합계
  const estimateSummary = useMemo(() => {
    const count = rows.length;
    const qtySum = rows.reduce((acc, r) => acc + Number(r.estimate_quantity || 0), 0);
    const amtSum = rows.reduce((acc, r) => acc + Number(r.est_total_amount || 0), 0);
    const krwSum = rows.reduce((acc, r) => acc + Number(r.est_total_amount_krw || 0), 0);
    const money = (n: number) => n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
    const intFmt = (n: number) => n.toLocaleString();
    return [
      { label: "견적 건수", value: intFmt(count), suffix: "건" },
      { label: "견적수량 합계", value: intFmt(qtySum) },
      { label: "공급가액 합계", value: money(amtSum) },
      { label: "원화환산공급가액 합계", value: money(krwSum) },
    ];
  }, [rows]);

  // ─── 다이얼로그 열기 ────────────────────────────────────────

  const openCreate = async () => {
    setDialogMode("create");
    const contractNo = await salesEstimateApi.generateNumber().catch(() => "");
    setContractNoDisplay(contractNo);
    setForm({
      ...DEFAULT_FORM,
      receipt_date: new Date().toISOString().slice(0, 10),
      contract_no: contractNo,
      items: [{ ...EMPTY_ITEM }],
    });
    setDialogOpen(true);
  };

  const openEdit = async () => {
    if (!selected) { toast.warning("수정할 견적을 선택하세요."); return; }
    setDialogMode("edit");
    try {
      const detail = await salesEstimateApi.detail(selected.objid);
      if (!detail) { toast.error("견적 상세 조회 실패"); return; }
      setContractNoDisplay(detail.contract_no ?? "");
      setForm({
        contract_no: detail.contract_no ?? "",
        category_cd: detail.category_cd ?? "",
        area_cd: detail.area_cd ?? "",
        customer_objid: detail.customer_objid ?? "",
        paid_type: detail.paid_type ?? "paid",
        receipt_date: detail.receipt_date ?? "",
        contract_currency: detail.contract_currency ?? "",
        exchange_rate: detail.exchange_rate ?? "",
        approval_required: detail.approval_required ?? "N",
        items: (detail.items ?? []).map((it: any) => ({
          objid: it.objid,
          seq: it.seq,
          product: it.product ?? "",
          part_objid: it.part_objid ?? "",
          part_no: it.master_part_no ?? it.part_no ?? "",
          part_name: it.master_part_name ?? it.part_name ?? "",
          serials: it.serials ?? [],
          quantity: it.quantity ?? "",
          due_date: it.due_date ?? "",
          return_reason: it.return_reason ?? "",
          customer_request: it.customer_request ?? "",
        })),
      });
      setDialogOpen(true);
    } catch {
      toast.error("견적 상세 조회 실패");
    }
  };

  // ─── 저장 ───────────────────────────────────────────────────
  // wace estimateRegistFormPopup 검증: 주문유형/국내해외/고객사/유무상/접수일/결재여부 필수,
  //   라인은 제품구분/품번/품명 필수.

  const handleSave = async () => {
    if (!form.category_cd)    { toast.error("주문유형을 선택하세요."); return; }
    if (!form.area_cd)        { toast.error("국내/해외를 선택하세요."); return; }
    if (!form.customer_objid) { toast.error("고객사를 선택하세요."); return; }
    if (!form.paid_type)      { toast.error("유/무상을 선택하세요."); return; }
    if (!form.receipt_date)   { toast.error("접수일을 입력하세요."); return; }
    if (!form.approval_required) { toast.error("결재여부를 선택하세요."); return; }
    const items = form.items ?? [];
    if (items.length === 0)   { toast.error("품목을 1건 이상 입력하세요."); return; }
    for (const it of items) {
      if (!it.product)    { toast.error(`라인 ${it.seq}: 제품구분 필수`); return; }
      if (!it.part_objid) { toast.error(`라인 ${it.seq}: 품번 필수`); return; }
      if (!it.part_name)  { toast.error(`라인 ${it.seq}: 품명 필수`); return; }
    }
    setSaving(true);
    try {
      if (dialogMode === "create") {
        await salesEstimateApi.create(form);
        toast.success("견적요청이 등록되었습니다.");
      } else if (selected) {
        await salesEstimateApi.update(selected.objid, form);
        toast.success("견적요청이 수정되었습니다.");
      }
      setDialogOpen(false);
      setSelected(null);
      await fetchList();
    } catch (err: any) {
      toast.error(`저장 실패: ${err?.response?.data?.message ?? err.message}`);
    } finally {
      setSaving(false);
    }
  };

  // ─── 삭제 ───────────────────────────────────────────────────

  const handleDelete = async () => {
    if (!selected) { toast.warning("삭제할 견적을 선택하세요."); return; }
    const ok = await confirm("견적 삭제", { description: `${selected.contract_no ?? selected.objid} 을(를) 삭제하시겠습니까?`, variant: "destructive" });
    if (!ok) return;
    try {
      await salesEstimateApi.remove(selected.objid);
      toast.success("삭제되었습니다.");
      setSelected(null);
      await fetchList();
    } catch (err: any) {
      toast.error(`삭제 실패: ${err?.response?.data?.message ?? err.message}`);
    }
  };

  // ─── 결재상신 (G4/G11 동일 패턴 — 견적, target_type='CONTRACT_ESTIMATE') ─────
  // wace estimateList_new.jsp:155 btnApproval + :887 fn_openAmaranthApproval 1:1.
  // 단순 SSO 흐름만 — 사전판정(checkApprovalRequired) 분기는 G4 영역으로 분리.
  //   가드: 행 미선택 / est_objid 없음(견적서 미작성) / inProcess·complete / 결재불필요
  const handleAmaranthApproval = async () => {
    if (!selected) { toast.warning("결재상신할 행을 선택해주십시오."); return; }
    if (!selected.est_objid) { toast.warning("견적서를 먼저 작성해주세요."); return; }
    const amaranthStatus = String(selected.amaranth_status ?? "");
    if (amaranthStatus === "inProcess")   { toast.warning("결재 진행중인 건은 상신할 수 없습니다."); return; }
    if (amaranthStatus === "complete")    { toast.warning("결재 완료된 건은 상신할 수 없습니다."); return; }
    if (amaranthStatus === "notRequired" || selected.approval_required === "N") {
      toast.warning("결재불필요로 처리된 건입니다."); return;
    }
    const ok = await confirm("결재상신", { description: "결재상신 하시겠습니까?" });
    if (!ok) return;
    try {
      const { fullUrl } = await salesEstimateApi.startApproval(selected.objid, {
        approvalTitle: `견적서 결재${selected.contract_no ? " - " + selected.contract_no : ""}`,
      });
      if (!fullUrl) { toast.error("결재 SSO URL을 받지 못했습니다."); return; }
      window.open(fullUrl, "amaranthApproval", "width=1200,height=900,scrollbars=yes,resizable=yes");
      await fetchList();
    } catch (err: any) {
      toast.error(err?.response?.data?.message ?? "결재 시스템 연동 중 오류가 발생했습니다.");
    }
  };

  // ─── 메일 발송 ──────────────────────────────────────────────
  // 실제 SMTP는 backend-node SMTP_SEND_SWITCH='Y'일 때 sales 계정(sales@rps-korea.com)으로 발송.
  // PDF 첨부: 견적관리 → "메일발송" 클릭 → EstimateMailDialog가
  //   최신 차수 template1/template2 페이지를 hidden iframe에 렌더 →
  //   fn_generateAndUploadPdf로 base64 추출 → backend가 estimate02 N건과 합본 첨부.

  const openMailDialog = () => {
    if (!selected) {
      toast.warning("메일 발송할 견적을 선택하세요.");
      return;
    }
    setMailDialogOpen(true);
  };

  // ─── 라인 편집 ──────────────────────────────────────────────

  const updateItem = (idx: number, key: keyof EstimateItem, val: any) => {
    setForm((prev) => {
      const items = [...(prev.items ?? [])];
      items[idx] = { ...items[idx], [key]: val };
      return { ...prev, items };
    });
  };
  const addItem = () => {
    setForm((prev) => ({
      ...prev,
      items: [...(prev.items ?? []), { ...EMPTY_ITEM, seq: (prev.items?.length ?? 0) + 1 }],
    }));
  };
  const removeItem = (idx: number) => {
    setForm((prev) => {
      const items = (prev.items ?? []).filter((_, i) => i !== idx).map((it, i) => ({ ...it, seq: i + 1 }));
      return { ...prev, items };
    });
  };

  // S/N 관리 (wace fn_openItemSnPopup): 라인별 시리얼 추가/삭제 + 연속번호생성
  const openSerialDialog = (idx: number) => {
    const item = form.items?.[idx];
    setSerialDialogIdx(idx);
    setSerialDraft([...(item?.serials ?? [])]);
    setSerialInput("");
    setSerialDialogOpen(true);
  };
  const addSerialDraft = () => {
    const v = serialInput.trim();
    if (!v) { toast.warning("S/N을 입력해주세요."); return; }
    if (serialDraft.includes(v)) { toast.warning("이미 등록된 S/N입니다."); return; }
    setSerialDraft((prev) => [...prev, v]);
    setSerialInput("");
  };
  const removeSerialDraft = (i: number) => {
    setSerialDraft((prev) => prev.filter((_, k) => k !== i));
  };
  const applySerialDraft = () => {
    if (serialDialogIdx === null) return;
    updateItem(serialDialogIdx, "serials", [...serialDraft]);
    setSerialDialogOpen(false);
  };

  // 연속번호 생성 (wace fn_openItemSequentialSnPopup + fn_generateItemSequentialSn)
  const openSeqDialog = () => {
    setSeqStartNo("");
    setSeqCount("");
    setSeqDialogOpen(true);
  };
  const generateSequentialSn = () => {
    const startNo = seqStartNo.trim();
    const count = parseInt(seqCount, 10);
    if (!startNo)        { toast.warning("시작 번호를 입력해주세요."); return; }
    if (!count || count < 1) { toast.warning("생성 개수를 1 이상 입력해주세요."); return; }
    if (count > 100)     { toast.warning("최대 100개까지만 생성 가능합니다."); return; }
    // wace 정규식: prefix + 숫자
    const m = startNo.match(/^(.*?)(\d+)$/);
    if (!m) { toast.warning("올바른 형식이 아닙니다. 마지막에 숫자가 있어야 합니다. (예: ITEM-001)"); return; }
    const prefix = m[1];
    const startNum = parseInt(m[2], 10);
    const numLength = m[2].length;
    setSerialDraft((prev) => {
      const next = [...prev];
      for (let i = 0; i < count; i++) {
        const padded = String(startNum + i).padStart(numLength, "0");
        const sn = prefix + padded;
        if (!next.includes(sn)) next.push(sn);
      }
      return next;
    });
    setSeqDialogOpen(false);
  };

  // ─── 렌더 ───────────────────────────────────────────────────

  const apprStatusOpts: SmartSelectOption[] = [
    { code: "작성중",     label: "작성중" },
    { code: "결재중",     label: "결재중" },
    { code: "결재완료",   label: "결재완료" },
    { code: "반려",       label: "반려" },
    { code: "결재불필요", label: "결재불필요" },
  ];

  const handleReset = () => setSearchForm({
    category_cd: "", customer_objid: "",
    search_partObjId: "", search_partName: "", search_serialNo: "",
    appr_status: "",
    receipt_start_date: "", receipt_end_date: "",
  });

  return (
    <div className="flex h-full flex-col overflow-hidden p-2 gap-2">
      {ConfirmDialogComponent}

      <PageHeader
        loading={loading}
        onSearch={fetchList}
        onReset={handleReset}
        actions={
        <>
          <Button size="sm" variant="destructive" className="h-8 gap-1 text-xs" onClick={handleDelete} disabled={!selected}>
            <Trash2 className="h-3.5 w-3.5" />삭제
          </Button>
          <Button size="sm" className="h-8 gap-1 text-xs" onClick={() => { if (selected) openEdit(); else openCreate(); }}>
            {selected ? <Pencil className="h-3.5 w-3.5" /> : <Plus className="h-3.5 w-3.5" />}
            {selected ? "견적요청수정" : "견적요청등록"}
          </Button>
          <Button size="sm" variant="outline" className="h-8 gap-1 text-xs" onClick={openTemplateChoice} disabled={!selected}>
            <Pencil className="h-3.5 w-3.5" />견적작성
          </Button>
          <Button size="sm" className="h-8 gap-1 bg-sky-600 hover:bg-sky-700 text-white text-xs"
                  onClick={handleAmaranthApproval} disabled={!selected}>
            <Send className="h-3.5 w-3.5" />결재상신
          </Button>
          <Button size="sm" variant="outline" className="h-8 gap-1 text-xs" disabled={!selected} onClick={openMailDialog}>
            메일발송
          </Button>
        </>
      } />

      <CompactFilterBar
        totalText={<>총 {rows.length.toLocaleString()}건</>}
      >
        <CompactFilterField label="주문유형" width={130}>
          <CommCodeSelect groupId="0000167"
            value={searchForm.category_cd}
            onValueChange={(v) => setSearchForm({ ...searchForm, category_cd: v })} />
        </CompactFilterField>
        <CompactFilterField label="고객사" width={160}>
          <CustomerSelect
            value={searchForm.customer_objid}
            onValueChange={(v) => setSearchForm({ ...searchForm, customer_objid: v })} />
        </CompactFilterField>
        <CompactFilterField label="품번" width={130}>
          <PartSelect mode="partNo"
            value={searchForm.search_partObjId}
            onValueChange={(v) => setSearchForm({ ...searchForm, search_partObjId: v })} />
        </CompactFilterField>
        <CompactFilterField label="품명" width={150}>
          <PartSelect mode="partName"
            value={searchForm.search_partObjId}
            onValueChange={(v) => setSearchForm({ ...searchForm, search_partObjId: v })} />
        </CompactFilterField>
        <CompactFilterField label="S/N" width={120}>
          <Input value={searchForm.search_serialNo}
                 onChange={(e) => setSearchForm({ ...searchForm, search_serialNo: e.target.value })} />
        </CompactFilterField>
        <CompactFilterField label="결재상태" width={120}>
          <SmartSelect
            options={apprStatusOpts}
            value={searchForm.appr_status}
            onValueChange={(v) => setSearchForm({ ...searchForm, appr_status: v })}
            placeholder="전체"
          />
        </CompactFilterField>
        <CompactFilterField label="접수일" width={280}>
          <CompactDateRange
            from={searchForm.receipt_start_date}
            setFrom={(v) => setSearchForm({ ...searchForm, receipt_start_date: v })}
            to={searchForm.receipt_end_date}
            setTo={(v) => setSearchForm({ ...searchForm, receipt_end_date: v })}
          />
        </CompactFilterField>
      </CompactFilterBar>

      {/* 그리드 — 첫 컬럼 체크박스 (행 아무 셀 클릭으로 단일 선택, 클립/폴더 등 팝업 컬럼은 stopPropagation으로 제외) */}
      <DataGrid
        gridId="sales-estimate"
        columns={gridColumns}
        data={rows}
        showCheckbox
        checkedIds={selected ? [selected.objid] : []}
        onCheckedChange={(ids) => {
          if (ids.length === 0) { setSelected(null); return; }
          const last = ids[ids.length - 1];
          setSelected(rows.find((r) => r.id === last) ?? null);
        }}
        selectedId={selected ? selected.objid : null}
        onSelect={(id) => {
          setSelected(id ? rows.find((r) => r.id === id) ?? null : null);
        }}
        onRowDoubleClick={(row) => { setSelected(row); openEdit(); }}
        emptyMessage={loading ? "조회 중..." : "데이터가 없습니다"}
        loading={loading}
        showColumnSettings
        paginationStyle="range"
        pageSizeOptions={[10, 15, 20, 50, 100]}
        summaryStats={estimateSummary}
        systemColumnKeys={["writer_name"]}
        onRefresh={fetchList}
        onDownload={() => {
          if (rows.length === 0) { toast.info("내보낼 데이터가 없습니다."); return; }
          const exportRows = rows.map((r) => {
            const out: Record<string, any> = {};
            GRID_COLUMNS.forEach((col) => { out[col.label] = r[col.key] ?? ""; });
            return out;
          });
          exportToExcel(exportRows, "견적관리.xlsx", "견적관리");
        }}
        showChart
      />

      {/* 등록/수정 Dialog */}
      <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
        <DialogContent
          className="!max-w-[95vw] w-[95vw] max-h-[92vh] overflow-y-auto"
          onInteractOutside={(e) => e.preventDefault()}  /* 자식 S/N·연속번호 Dialog 닫힐 때 부모까지 닫히는 현상 차단 */
        >
          <DialogHeader>
            <DialogTitle>{dialogMode === "create" ? "견적 등록" : "견적 수정"}</DialogTitle>
            <DialogDescription>
              
            </DialogDescription>
          </DialogHeader>

          {/* 견적요청 기본정보 — wace estimateRegistFormPopup.jsp 1행/2행 (8개) */}
          <fieldset className="border rounded-md p-3">
            <legend className="text-sm font-semibold px-2">견적요청 기본정보</legend>
            <div className="grid grid-cols-4 gap-3">
              <div>
                <Label className="text-xs">영업번호</Label>
                <Input readOnly className="bg-muted/30" value={contractNoDisplay} placeholder="저장 시 자동 부여" />
              </div>
              <div>
                <Label className="text-xs">주문유형 <span className="text-rose-600">*</span></Label>
                <CommCodeSelect groupId="0000167"
                  value={form.category_cd}
                  onValueChange={(v) => setForm({ ...form, category_cd: v })} />
              </div>
              <div>
                <Label className="text-xs">국내/해외 <span className="text-rose-600">*</span></Label>
                <CommCodeSelect groupId="0001219"
                  value={form.area_cd}
                  onValueChange={(v) => setForm({ ...form, area_cd: v })} />
              </div>
              <div>
                <Label className="text-xs">고객사 <span className="text-rose-600">*</span></Label>
                <CustomerSelect
                  value={form.customer_objid}
                  onValueChange={(v) => setForm({ ...form, customer_objid: v })} />
              </div>
              <div>
                <Label className="text-xs">유/무상 <span className="text-rose-600">*</span></Label>
                <Select value={form.paid_type || undefined}
                        onValueChange={(v) => setForm({ ...form, paid_type: v })}>
                  <SelectTrigger><SelectValue placeholder="선택" /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="paid">유상</SelectItem>
                    <SelectItem value="free">무상</SelectItem>
                  </SelectContent>
                </Select>
              </div>
              <div>
                <Label className="text-xs">접수일 <span className="text-rose-600">*</span></Label>
                <Input type="date" value={form.receipt_date}
                       onChange={(e) => setForm({ ...form, receipt_date: e.target.value })} />
              </div>
              <div>
                <Label className="text-xs">견적환종</Label>
                <CommCodeSelect groupId="0001533"
                  value={form.contract_currency ?? ""}
                  onValueChange={(v) => setForm({ ...form, contract_currency: v })} />
              </div>
              <div>
                <Label className="text-xs">견적환율</Label>
                <Input value={form.exchange_rate ?? ""}
                       onChange={(e) => setForm({ ...form, exchange_rate: e.target.value })} />
              </div>
              <div className="col-span-4">
                <Label className="text-xs">결재여부 <span className="text-rose-600">*</span></Label>
                <div className="flex items-center gap-4 pt-1">
                  <label className="flex items-center gap-1.5 cursor-pointer text-sm">
                    <input type="checkbox" checked={form.approval_required === "Y"}
                           onChange={() => setForm({ ...form, approval_required: "Y" })} /> 필요
                  </label>
                  <label className="flex items-center gap-1.5 cursor-pointer text-sm">
                    <input type="checkbox" checked={form.approval_required === "N"}
                           onChange={() => setForm({ ...form, approval_required: "N" })} /> 불필요
                  </label>
                </div>
              </div>
            </div>
          </fieldset>

          {/* 품목정보 — wace 8컬럼 (제품구분/품번/품명/S/N/견적수량/요청납기/반납사유/고객요청사항) */}
          <fieldset className="border rounded-md p-3 space-y-2">
            <legend className="text-sm font-semibold px-2 flex items-center justify-between w-full">
              <span>품목정보</span>
            </legend>
            {isContractConfirmed && (
              <p className="text-[11px] text-rose-600">
                ※ 수주확정된 건은 품목 추가/삭제가 불가합니다. (프로젝트 자동생성과 정합성 보호)
              </p>
            )}
            <div className="overflow-x-auto">
              <table className="w-full text-xs">
                <thead className="bg-muted/50">
                  <tr>
                    <th className="p-2 w-10 whitespace-nowrap">No</th>
                    <th className="p-2 w-28 whitespace-nowrap">제품구분 <span className="text-rose-600">*</span></th>
                    <th className="p-2 w-40 whitespace-nowrap">품번 <span className="text-rose-600">*</span></th>
                    <th className="p-2 w-56 whitespace-nowrap">품명 <span className="text-rose-600">*</span></th>
                    <th className="p-2 w-40 whitespace-nowrap">S/N</th>
                    <th className="p-2 w-20 whitespace-nowrap">견적수량</th>
                    <th className="p-2 w-36 whitespace-nowrap">요청납기</th>
                    <th className="p-2 w-28 whitespace-nowrap">반납사유</th>
                    <th className="p-2 min-w-[260px] whitespace-nowrap">고객요청사항</th>
                    <th className="p-2 w-12 whitespace-nowrap">삭제</th>
                  </tr>
                </thead>
                <tbody>
                  {(form.items ?? []).map((it, idx) => (
                    <tr key={idx} className="border-t">
                      <td className="p-1 text-center">{it.seq}</td>
                      <td className="p-1">
                        <CommCodeSelect groupId="0000001"
                          value={it.product}
                          onValueChange={(v) => updateItem(idx, "product", v)} />
                      </td>
                      <td className="p-1">
                        <PartSelect
                          mode="partNo"
                          value={it.part_objid}
                          onValueChange={(partObjId, row) => {
                            setForm((prev) => {
                              const items = [...(prev.items ?? [])];
                              items[idx] = {
                                ...items[idx],
                                part_objid: partObjId,
                                part_no: row?.item_number ?? "",
                                part_name: row?.item_name ?? items[idx].part_name,
                              };
                              return { ...prev, items };
                            });
                          }}
                        />
                      </td>
                      <td className="p-1">
                        <PartSelect
                          mode="partName"
                          value={it.part_objid}
                          onValueChange={(partObjId, row) => {
                            setForm((prev) => {
                              const items = [...(prev.items ?? [])];
                              items[idx] = {
                                ...items[idx],
                                part_objid: partObjId,
                                part_no: row?.item_number ?? items[idx].part_no,
                                part_name: row?.item_name ?? "",
                              };
                              return { ...prev, items };
                            });
                          }}
                        />
                      </td>
                      <td className="p-1">
                        <Input className="h-8" readOnly
                               value={(it.serials ?? []).join(", ")}
                               onClick={() => openSerialDialog(idx)}
                               placeholder="클릭하여 S/N 추가" />
                      </td>
                      <td className="p-1">
                        <Input className="h-8 text-right" type="number" min={0}
                               value={it.quantity ?? ""}
                               onChange={(e) => updateItem(idx, "quantity", e.target.value)} />
                      </td>
                      <td className="p-1">
                        <Input className="h-8" type="date"
                               value={it.due_date ?? ""}
                               onChange={(e) => updateItem(idx, "due_date", e.target.value)} />
                      </td>
                      <td className="p-1">
                        <CommCodeSelect groupId="0001810"
                          value={it.return_reason ?? ""}
                          onValueChange={(v) => updateItem(idx, "return_reason", v)} />
                      </td>
                      <td className="p-1">
                        <Textarea className="min-h-[34px] resize-y text-xs"
                                  rows={1}
                                  value={it.customer_request ?? ""}
                                  onChange={(e) => updateItem(idx, "customer_request", e.target.value)} />
                      </td>
                      <td className="p-1 text-center">
                        <Button variant="ghost" size="icon" onClick={() => removeItem(idx)} disabled={isContractConfirmed}>
                          <Trash2 className="w-3 h-3" />
                        </Button>
                      </td>
                    </tr>
                  ))}
                  {(!form.items || form.items.length === 0) && (
                    <tr><td colSpan={10} className="p-3 text-center text-muted-foreground">품목이 없습니다.</td></tr>
                  )}
                </tbody>
              </table>
            </div>
            <div className="flex gap-2">
              <Button size="sm" variant="outline" onClick={addItem} disabled={isContractConfirmed}>
                <Plus className="w-3 h-3 mr-1" />품목 추가
              </Button>
            </div>
          </fieldset>

          <DialogFooter>
            <Button variant="outline" onClick={() => setDialogOpen(false)} disabled={saving}>취소</Button>
            <Button onClick={handleSave} disabled={saving}>
              {saving ? <Loader2 className="w-4 h-4 mr-1 animate-spin" /> : <Save className="w-4 h-4 mr-1" />}
              저장
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 메일 발송 Dialog — wace estimateMailFormPopup.jsp 1:1 */}
      <EstimateMailDialog
        open={mailDialogOpen}
        onOpenChange={setMailDialogOpen}
        contractObjid={selected?.objid ?? null}
        addEstCount={Number((selected as any)?.add_est_cnt ?? 0)}
        estStatusCount={Number(selected?.est_status ?? 0)}
        onSent={() => { fetchList(); }}
      />

      {/* 첨부파일 다이얼로그 — 추가견적 등 클립 컬럼 클릭 시 */}
      {attachContext && (
        <AttachmentDialog
          open={attachDialogOpen}
          onOpenChange={setAttachDialogOpen}
          targetObjid={attachContext.targetObjid}
          docType={attachContext.docType}
          uploadDocType={attachContext.uploadDocType}
          uploadDocTypeName={attachContext.uploadDocTypeName}
          title={attachContext.title}
          onChanged={fetchList}
        />
      )}

      {/* G5: 견적작성 — 일반/장비 선택 (wace estimateList_new.jsp Swal 105~106) */}
      <Dialog open={templateChoiceOpen} onOpenChange={setTemplateChoiceOpen}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>견적서 작성</DialogTitle>
            <DialogDescription>작성할 견적서 종류를 선택하세요.</DialogDescription>
          </DialogHeader>
          <div className="flex gap-3 justify-center py-4">
            <Button variant="default" size="lg" onClick={() => pickTemplate("1")}>일반 견적서</Button>
            <Button size="lg" onClick={() => pickTemplate("2")} style={{ backgroundColor: "#28a745" }}>장비 견적서</Button>
          </div>
        </DialogContent>
      </Dialog>

      {/* G5: 견적 차수 리스트 (wace fn_showEstimateList) */}
      <Dialog open={templateListOpen} onOpenChange={setTemplateListOpen}>
        <DialogContent className="sm:max-w-2xl">
          <DialogHeader>
            <DialogTitle>견적 차수 리스트 — {selected?.contract_no ?? ""}</DialogTitle>
            <DialogDescription>차수를 선택하면 해당 견적서를 새 창으로 엽니다.</DialogDescription>
          </DialogHeader>
          <div className="max-h-[60vh] overflow-y-auto">
            <table className="w-full text-sm border-collapse">
              <thead>
                <tr className="bg-muted">
                  <th className="border px-2 py-1.5 text-center w-[60px]">차수</th>
                  <th className="border px-2 py-1.5 text-center w-[80px]">종류</th>
                  <th className="border px-2 py-1.5">견적번호</th>
                  <th className="border px-2 py-1.5 text-right w-[120px]">공급가액</th>
                  <th className="border px-2 py-1.5 text-center w-[140px]">작성일시</th>
                  <th className="border px-2 py-1.5 text-center w-[80px]">작성자</th>
                </tr>
              </thead>
              <tbody>
                {templateList.length === 0 ? (
                  <tr><td colSpan={6} className="text-center py-6 text-muted-foreground">등록된 견적서가 없습니다.</td></tr>
                ) : templateList.map((t, i) => (
                  <tr key={t.objid}
                      className="cursor-pointer hover:bg-accent"
                      onClick={() => openExistingTemplate(t.objid, t.template_type)}>
                    <td className="border px-2 py-1.5 text-center">{templateList.length - i}차</td>
                    <td className="border px-2 py-1.5 text-center">{t.template_type === "2" ? "장비" : "일반"}</td>
                    <td className="border px-2 py-1.5">{t.estimate_no ?? ""}</td>
                    <td className="border px-2 py-1.5 text-right">{t.total_amount ? Number(String(t.total_amount).replace(/,/g, "")).toLocaleString() : ""}</td>
                    <td className="border px-2 py-1.5 text-center">{t.regdate ?? ""}</td>
                    <td className="border px-2 py-1.5 text-center">{t.writer ?? ""}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </DialogContent>
      </Dialog>

      {/* 품목 검색 — 라인 클릭 시 해당 라인에 part_objid/part_no/part_name 채움 (단일 선택) */}
      <ItemSearchDialog
        open={itemDialogOpen}
        onOpenChange={(o) => { setItemDialogOpen(o); if (!o) setItemSearchTargetIdx(null); }}
        onSelect={(items: ItemRow[]) => {
          if (!items || items.length === 0) return;
          const it = items[0];
          if (itemSearchTargetIdx === null) {
            // 라인이 비어있으면 새 라인으로 추가
            setForm((prev) => ({
              ...prev,
              items: [...(prev.items ?? []), {
                ...EMPTY_ITEM,
                seq: (prev.items?.length ?? 0) + 1,
                part_objid: String(it.objid ?? ""),
                part_no: it.item_number ?? it.item_code ?? "",
                part_name: it.item_name ?? "",
              }],
            }));
          } else {
            const idx = itemSearchTargetIdx;
            setForm((prev) => {
              const items = [...(prev.items ?? [])];
              items[idx] = {
                ...items[idx],
                part_objid: String(it.objid ?? ""),
                part_no: it.item_number ?? it.item_code ?? "",
                part_name: it.item_name ?? "",
              };
              return { ...prev, items };
            });
          }
          setItemSearchTargetIdx(null);
        }}
      />

      {/* S/N 관리 — wace fn_openItemSnPopup (테이블 + 연속번호생성) */}
      <Dialog open={serialDialogOpen} onOpenChange={setSerialDialogOpen}>
        <DialogContent className="max-w-2xl" onInteractOutside={(e) => e.preventDefault()}>
          <DialogHeader>
            <DialogTitle className="text-center">S/N 관리</DialogTitle>
            <DialogDescription className="sr-only">시리얼 번호 추가/삭제 및 연속번호 생성</DialogDescription>
          </DialogHeader>
          <div className="space-y-4">
            {/* S/N 목록 테이블 */}
            <div className="max-h-[300px] overflow-y-auto border rounded">
              <table className="w-full text-sm">
                <thead className="bg-muted/50">
                  <tr>
                    <th className="border px-3 py-2 w-16 text-center">번호</th>
                    <th className="border px-3 py-2 text-center">S/N</th>
                    <th className="border px-3 py-2 w-20 text-center">삭제</th>
                  </tr>
                </thead>
                <tbody>
                  {serialDraft.length === 0 ? (
                    <tr><td colSpan={3} className="border px-3 py-8 text-center text-muted-foreground">등록된 S/N이 없습니다.</td></tr>
                  ) : serialDraft.map((s, i) => (
                    <tr key={i}>
                      <td className="border px-3 py-1.5 text-center">{i + 1}</td>
                      <td className="border px-3 py-1.5">{s}</td>
                      <td className="border px-3 py-1.5 text-center">
                        <Button size="sm" variant="outline" onClick={() => removeSerialDraft(i)}>삭제</Button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            {/* S/N 입력 + 추가 */}
            <div className="flex gap-2">
              <Input value={serialInput} onChange={(e) => setSerialInput(e.target.value)}
                     onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addSerialDraft(); } }}
                     placeholder="S/N 입력" />
              <Button onClick={addSerialDraft} type="button">추가</Button>
            </div>
          </div>
          <DialogFooter className="sm:justify-center">
            <Button variant="outline" onClick={openSeqDialog}>연속번호생성</Button>
            <Button onClick={applySerialDraft}>확인</Button>
            <Button variant="outline" onClick={() => setSerialDialogOpen(false)}>취소</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 연속번호 생성 — wace fn_openItemSequentialSnPopup */}
      <Dialog open={seqDialogOpen} onOpenChange={setSeqDialogOpen}>
        <DialogContent className="max-w-sm" onInteractOutside={(e) => e.preventDefault()}>
          <DialogHeader>
            <DialogTitle>연속번호 생성</DialogTitle>
            <DialogDescription className="sr-only">시작번호와 생성개수로 연속 S/N 일괄 추가</DialogDescription>
          </DialogHeader>
          <div className="space-y-3">
            <div>
              <Label className="text-xs">시작번호 <span className="text-rose-600">*</span></Label>
              <Input value={seqStartNo} onChange={(e) => setSeqStartNo(e.target.value)}
                     placeholder="예: ITEM-001" />
            </div>
            <div>
              <Label className="text-xs">생성개수 <span className="text-rose-600">*</span></Label>
              <Input type="number" min={1} max={100}
                     value={seqCount} onChange={(e) => setSeqCount(e.target.value)}
                     placeholder="예: 10" />
            </div>
            <div className="bg-muted/40 rounded p-2 text-[11px] leading-5 text-muted-foreground">
              예: ITEM-001, 개수 3 → ITEM-001, ITEM-002, ITEM-003<br />
              ※ 최대 100개까지 생성 가능
            </div>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setSeqDialogOpen(false)}>취소</Button>
            <Button onClick={generateSequentialSn}>생성</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
