import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { ChatResponse } from "./api/chat";
import { chatApi } from "./api/chat";
import type { PrRow } from "./api/form";
import type { ThreadItem } from "./types";
import type { PurchaseFormSnapshot } from "./types/purchasePayload";
import { AnalysisCard } from "./components/AnalysisCard";
import { DisambiguationCard } from "./components/DisambiguationCard";
import { Header } from "./components/Header";
import { LandingHero } from "./components/LandingHero";
import { LoadingCard } from "./components/LoadingCard";
import { MainChatComposer } from "./components/MainChatComposer";
import { PayloadViewerModal } from "./components/PayloadViewerModal";
import { PrLineItemsTable } from "./components/PrLineItemsTable";
import { ProcurementFormBlock } from "./components/ProcurementFormBlock";
import { RequestSummaryPanel } from "./components/RequestSummaryPanel";
import { SuggestionCards } from "./components/SuggestionCards";
import { UserBubble } from "./components/UserBubble";
import { Icon } from "./components/Icon";

function uid(): string {
  return crypto.randomUUID();
}

function isoDateToday(): string {
  const d = new Date();
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

function makeRequestNumber(): string {
  return `PR-${new Date().getFullYear()}-${crypto.randomUUID().replace(/-/g, "").slice(0, 6).toUpperCase()}`;
}

/** Demo defaults so Request details are not empty on load / after New Requisition. */
const DEFAULT_REQUEST_AUTOFILL: {
  requestedBy: string;
  department: string;
  budgetCode: string;
} = {
  requestedBy: "Alex Morgan (Corporate Procurement)",
  department: "Facilities & Workplace — NY Headquarters",
  budgetCode: "GL-6600-CAPEX-OFFICE",
};

function itemsFromResponse(res: ChatResponse): ThreadItem[] {
  const out: ThreadItem[] = [];

  let rows = res.analysis_rows ?? [];
  if (!rows.length && res.summary) {
    rows = [
      {
        icon: "info",
        left: res.summary,
        right: res.status === "not_found" ? "—" : "OK",
        right_style: "muted",
      },
    ];
  }
  if (res.error) {
    rows = [
      ...rows,
      {
        icon: "error",
        left: `Configuration / API: ${res.error}`,
        right: "—",
        right_style: "muted",
      },
    ];
  }

  if (rows.length) {
    out.push({ id: uid(), type: "analysis", rows });
  }

  if (res.status === "choose" && res.candidates?.length) {
    out.push({
      id: uid(),
      type: "pick",
      summary: res.summary,
      candidates: res.candidates,
    });
  }

  if (res.status === "found" && res.commodity_code) {
    const det = res.selected_details;
    const codeSummary = det
      ? `Seg ${det.segment_code ?? "—"} · Fam ${det.family_code ?? "—"} · Class ${det.class_code ?? "—"} · Commodity ${det.commodity_code}`
      : `Commodity ${res.commodity_code}`;
    const cc = res.commodity_code ?? det?.commodity_code;
    if (cc != null) {
      out.push({
        id: uid(),
        type: "form",
        commodityCode: cc,
        intro: res.form_intro || "Confirm specifications for this catalogue item.",
        catalogPath: det?.path,
        codeSummary,
        selected: det,
      });
    }
  }

  return out;
}

export default function App() {
  const [thread, setThread] = useState<ThreadItem[]>([]);
  const [draft, setDraft] = useState("");
  const [editingUserId, setEditingUserId] = useState<string | null>(null);
  const [editDraft, setEditDraft] = useState("");
  const [busy, setBusy] = useState(false);
  const [accumulatedPrRows, setAccumulatedPrRows] = useState<PrRow[]>([]);
  const [formSnapshots, setFormSnapshots] = useState<PurchaseFormSnapshot[]>(
    []
  );
  const [showFollowUpComposer, setShowFollowUpComposer] = useState(false);
  const [followUpDraft, setFollowUpDraft] = useState("");
  const [requestNumber, setRequestNumber] = useState(makeRequestNumber);
  const [requestedBy, setRequestedBy] = useState(
    DEFAULT_REQUEST_AUTOFILL.requestedBy
  );
  const [department, setDepartment] = useState(
    DEFAULT_REQUEST_AUTOFILL.department
  );
  const [budgetCode, setBudgetCode] = useState(
    DEFAULT_REQUEST_AUTOFILL.budgetCode
  );
  const [requestedDate, setRequestedDate] = useState(isoDateToday);
  const [submitFeedback, setSubmitFeedback] = useState<string | null>(null);
  const [payloadModalOpen, setPayloadModalOpen] = useState(false);
  const scrollRef = useRef<HTMLDivElement>(null);
  const followUpRef = useRef<HTMLDivElement>(null);

  const scrollToBottom = useCallback(() => {
    requestAnimationFrame(() => {
      const el = scrollRef.current;
      if (!el) return;
      el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
    });
  }, []);

  useEffect(() => {
    scrollToBottom();
  }, [thread.length, busy, accumulatedPrRows.length, scrollToBottom]);

  useEffect(() => {
    if (!showFollowUpComposer) return;
    const id = window.setTimeout(() => {
      followUpRef.current?.scrollIntoView({
        behavior: "smooth",
        block: "nearest",
      });
    }, 120);
    return () => window.clearTimeout(id);
  }, [showFollowUpComposer]);

  const handleCommitPurchase = useCallback(
    ({
      rows,
      snapshot,
    }: {
      rows: PrRow[];
      snapshot: PurchaseFormSnapshot;
    }) => {
      if (!rows.length) return;
      setFormSnapshots((prev) => [...prev, snapshot]);
      setAccumulatedPrRows((prev) => {
        const prior = [...prev];
        const base = prior.length
          ? Math.max(...prior.map((r) => Number(r.pr_line_item) || 0))
          : 0;
        const next = rows.map((r, i) => ({
          ...r,
          pr_line_item: base + 10 * (i + 1),
        }));
        return [...prior, ...next];
      });
    },
    []
  );

  const resetSession = useCallback(() => {
    setThread([]);
    setDraft("");
    setEditingUserId(null);
    setEditDraft("");
    setBusy(false);
    setAccumulatedPrRows([]);
    setFormSnapshots([]);
    setShowFollowUpComposer(false);
    setFollowUpDraft("");
    setSubmitFeedback(null);
    setPayloadModalOpen(false);
    setRequestNumber(makeRequestNumber());
    setRequestedBy(DEFAULT_REQUEST_AUTOFILL.requestedBy);
    setDepartment(DEFAULT_REQUEST_AUTOFILL.department);
    setBudgetCode(DEFAULT_REQUEST_AUTOFILL.budgetCode);
    setRequestedDate(isoDateToday());
  }, []);

  const exportPayloadJson = useMemo(() => {
    return JSON.stringify(
      {
        requestDetails: {
          requestNumber,
          requestedBy,
          department,
          budgetCode,
          requestedDate,
        },
        purchaseForms: formSnapshots,
        lineItems: accumulatedPrRows,
      },
      null,
      2
    );
  }, [
    requestNumber,
    requestedBy,
    department,
    budgetCode,
    requestedDate,
    formSnapshots,
    accumulatedPrRows,
  ]);

  const openAddLineComposer = useCallback(() => {
    setShowFollowUpComposer(true);
  }, []);

  const appendAssistant = useCallback(async (messageText: string, pick?: number | null) => {
    setBusy(true);
    try {
      const res = await chatApi({
        message: messageText,
        selected_commodity_code: pick ?? undefined,
      });
      const tail = itemsFromResponse(res);
      setThread((prev) => [...prev, ...tail]);
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      setThread((prev) => [
        ...prev,
        {
          id: uid(),
          type: "analysis",
          rows: [
            {
              icon: "error",
              left: msg,
              right: "ERROR",
              right_style: "muted",
            },
          ],
        },
      ]);
    } finally {
      setBusy(false);
    }
  }, []);

  const sendFollowUp = useCallback(async () => {
    const text = followUpDraft.trim();
    if (!text || busy) return;
    setFollowUpDraft("");
    setShowFollowUpComposer(false);
    const user: ThreadItem = { id: uid(), type: "user", text };
    setThread((prev) => [...prev, user]);
    await appendAssistant(text, null);
  }, [followUpDraft, busy, appendAssistant]);

  const beginEdit = useCallback((id: string, currentText: string) => {
    setEditingUserId(id);
    setEditDraft(currentText);
  }, []);

  const cancelEdit = useCallback(() => {
    setEditingUserId(null);
    setEditDraft("");
  }, []);

  const send = useCallback(async () => {
    const text = draft.trim();
    if (!text || busy) return;
    setDraft("");
    const user: ThreadItem = { id: uid(), type: "user", text };
    setThread((prev) => [...prev, user]);
    await appendAssistant(text, null);
  }, [draft, busy, appendAssistant]);

  const submitEdit = useCallback(async () => {
    const text = editDraft.trim();
    if (!text || !editingUserId || busy) return;
    const idx = thread.findIndex((t) => t.id === editingUserId);
    if (idx === -1) return;

    setEditingUserId(null);
    setEditDraft("");

    const prefix = thread.slice(0, idx);
    const user: ThreadItem = { id: uid(), type: "user", text };
    setThread([...prefix, user]);

    await appendAssistant(text, null);
  }, [editDraft, editingUserId, busy, thread, appendAssistant]);

  const onPick = useCallback(
    async (code: number) => {
      if (busy) return;
      await appendAssistant("", code);
    },
    [busy, appendAssistant]
  );

  const showLanding = thread.length === 0 && !busy;

  const handleSubmitRequest = useCallback(() => {
    if (accumulatedPrRows.length === 0) return;
    setSubmitFeedback(
      `Request ${requestNumber} is ready to submit (${accumulatedPrRows.length} line item${
        accumulatedPrRows.length === 1 ? "" : "s"
      }). Connect your approval workflow to finalize.`
    );
    window.setTimeout(() => setSubmitFeedback(null), 8000);
  }, [accumulatedPrRows.length, requestNumber]);

  return (
    <div className="bg-background text-on-background font-body-md overflow-hidden flex flex-col h-screen">
      <Header onNewRequisition={resetSession} />
      <main
        id="chat"
        className="relative flex min-h-0 flex-1 flex-col overflow-hidden bg-surface-container-lowest"
      >
        {showLanding ? (
          <div
            ref={scrollRef}
            className="mx-auto flex min-h-0 w-full max-w-6xl flex-1 flex-col overflow-y-auto px-gutter pb-8 pt-2"
          >
            <LandingHero />
            <MainChatComposer
              value={draft}
              onChange={setDraft}
              onSend={send}
            />
            <SuggestionCards onPick={(text) => setDraft(text)} />
          </div>
        ) : (
          <div className="flex min-h-0 flex-1 flex-col lg:flex-row">
            <div
              ref={scrollRef}
              className="flex min-h-0 min-w-0 flex-1 flex-col gap-6 overflow-y-auto border-outline-variant px-gutter py-4 pb-8 lg:border-r lg:py-6"
            >
              {thread.map((item) => {
                if (item.type === "user") {
                  const isEditing = editingUserId === item.id;
                  return (
                    <UserBubble
                      key={item.id}
                      text={item.text}
                      isEditing={isEditing}
                      editValue={isEditing ? editDraft : ""}
                      onEditChange={setEditDraft}
                      onBeginEdit={() => beginEdit(item.id, item.text)}
                      onSubmitEdit={submitEdit}
                      onCancelEdit={cancelEdit}
                    />
                  );
                }
                if (item.type === "analysis") {
                  return (
                    <AnalysisCard key={item.id} rows={item.rows} />
                  );
                }
                if (item.type === "pick") {
                  return (
                    <DisambiguationCard
                      key={item.id}
                      summary={item.summary}
                      candidates={item.candidates}
                      disabled={busy}
                      onPick={onPick}
                    />
                  );
                }
                return (
                  <ProcurementFormBlock
                    key={item.id}
                    commodityCode={item.commodityCode}
                    intro={item.intro}
                    catalogPath={item.catalogPath}
                    codeSummary={item.codeSummary}
                    onCommitPurchase={handleCommitPurchase}
                    onAddNew={openAddLineComposer}
                  />
                );
              })}
              {busy ? <LoadingCard /> : null}
              {showFollowUpComposer ? (
                <div
                  ref={followUpRef}
                  className="mt-2 rounded-2xl border border-outline-variant bg-surface-container-low p-4 shadow-sm"
                >
                  <p className="mb-3 font-body-sm text-secondary">
                    Add another catalogue line to this same purchase request.
                    Your chat continues here—this is not a new request.
                  </p>
                  <MainChatComposer
                    value={followUpDraft}
                    onChange={setFollowUpDraft}
                    onSend={sendFollowUp}
                  />
                </div>
              ) : null}
              <p className="text-center font-body-sm text-xs text-secondary/60">
                Procure AI can make mistakes. Check important procurement
                details before approval.
              </p>
            </div>

            <aside className="flex min-h-0 min-w-0 flex-1 flex-col gap-2 overflow-hidden bg-surface-container-low/60 px-gutter py-3 lg:py-4">
              <RequestSummaryPanel
                requestNumber={requestNumber}
                requestedBy={requestedBy}
                department={department}
                budgetCode={budgetCode}
                requestedDate={requestedDate}
                onRequestedByChange={setRequestedBy}
                onDepartmentChange={setDepartment}
                onBudgetCodeChange={setBudgetCode}
                onRequestedDateChange={setRequestedDate}
              />
              <div className="flex min-h-0 flex-1 flex-col gap-2">
                {accumulatedPrRows.length > 0 ? (
                  <PrLineItemsTable rows={accumulatedPrRows} />
                ) : (
                  <div className="flex min-h-[120px] flex-1 items-center justify-center rounded-xl border border-dashed border-outline-variant bg-white px-4 py-6 text-center text-xs text-secondary shadow-sm">
                    No line items yet. Confirm selections in the chat to populate
                    this table.
                  </div>
                )}
              </div>
              <div className="shrink-0 space-y-2 pt-1">
                <div className="flex gap-2">
                  <button
                    type="button"
                    disabled={accumulatedPrRows.length === 0}
                    onClick={handleSubmitRequest}
                    className="min-w-0 flex-1 rounded-xl bg-primary py-2.5 text-sm font-bold text-on-primary shadow-sm transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40"
                  >
                    Submit Request
                  </button>
                  <button
                    type="button"
                    title="Show payload JSON"
                    aria-label="Show payload JSON viewer"
                    onClick={() => setPayloadModalOpen(true)}
                    className="flex shrink-0 flex-col items-center justify-center gap-0.5 rounded-xl border border-outline-variant bg-white px-2.5 py-1.5 text-secondary shadow-sm transition-colors hover:bg-surface-container-high sm:flex-row sm:gap-1 sm:px-3 sm:py-2"
                  >
                    <Icon name="data_object" className="text-lg sm:text-xl" />
                    <span className="max-w-[4.5rem] text-center font-label-caps text-[8px] font-bold uppercase leading-tight tracking-wide">
                      Show Payload
                    </span>
                  </button>
                </div>
                {submitFeedback ? (
                  <p className="text-center text-xs text-primary">{submitFeedback}</p>
                ) : null}
              </div>
            </aside>
          </div>
        )}
      </main>
      <PayloadViewerModal
        open={payloadModalOpen}
        title="Request payload (JSON)"
        jsonText={exportPayloadJson}
        onClose={() => setPayloadModalOpen(false)}
      />
    </div>
  );
}
