import * as React from "react";
import {
  BrainCircuit,
  ChevronDown,
  Minus,
  RotateCcw,
  X,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Spinner } from "@/components/ui/spinner";
import { ChatInputArea } from "@/components/ui/chat-input-area";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ChatWidgetMessage } from "@/components/ui/chat-widget-primitives";
import {
  PolicySingleBankAnswer,
  PolicyComparisonTable,
  PolicyRankedList,
} from "./policy-ai-responses";
import type {
  PolicyAIMessage,
  PolicyQueryContext,
  PolicyResponseContent,
} from "./policy-ai-primitives";

// ---------------------------------------------------------------------------
// PolicyAIFAB props
// ---------------------------------------------------------------------------

export interface PolicyAIFABProps {
  onClick: () => void;
  hasNudge?: boolean;
  className?: string;
}

// ---------------------------------------------------------------------------
// PolicyAIPanelProps
// ---------------------------------------------------------------------------

export interface PolicyAIPanelProps {
  /** Required in float mode; ignored in `inline` mode. */
  open?: boolean;
  /** Required in float mode; ignored in `inline` mode. */
  onClose?: () => void;
  messages?: PolicyAIMessage[];
  suggestedQuestions?: Record<string, string[]>;
  isStreaming?: boolean;
  isLoading?: boolean;
  /**
   * Live thinking steps for the streaming reply, in the order received from the
   * SSE stream. The last item is rendered in-progress, earlier ones as done.
   * Falls back to a generic placeholder when empty. (Not used while `isLoading`.)
   */
  thinkingSteps?: string[];
  onSendMessage?: (text: string) => void;
  /** Called when the user selects files via the paperclip attachment button. */
  onAttachFile?: (files: FileList) => void;
  /** Called when the user selects images via the image upload button. */
  onAttachImage?: (files: FileList) => void;
  onReset?: () => void;
  /** True when older messages can be loaded (cursor pagination). */
  hasMoreMessages?: boolean;
  /** True while a `onLoadMoreMessages` request is in flight. */
  isLoadingMoreMessages?: boolean;
  /** Fired when the user scrolls near the top — fetch older messages. */
  onLoadMoreMessages?: () => void;
  /** Position relative to viewport — defaults to bottom-right. Ignored when `inline` is true. */
  position?: "bottom-right" | "bottom-left";
  /**
   * When true, renders as an inline block filling its parent container instead
   * of a fixed-position float widget. Use inside drawers, sheets, or tab panels.
   * Hides the minimize and close buttons.
   */
  inline?: boolean;
  className?: string;
}

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

type PolicyTypeTab =
  | "Income"
  | "Security"
  | "Serviceability"
  | "Loan Type"
  | "Borrower";

const DEFAULT_SUGGESTED: Record<PolicyTypeTab, string[]> = {
  Income: [
    "Which banks accept casual income?",
    "Which lenders accept bonus income, ranked best to worst?",
    "How is self-employed income assessed across lenders?",
  ],
  Security: [
    "Which banks accept apartments under 40sqm?",
    "What is the maximum LVR for a serviced apartment?",
    "Do any lenders accept hobby farm properties?",
  ],
  Serviceability: [
    "Which banks have the most flexible DTI ratio?",
    "How is HECS debt treated by different lenders?",
    "Which lenders apply the lowest assessment buffer rate?",
  ],
  "Loan Type": [
    "Which lenders offer cashback on refinances?",
    "Which banks accept low-doc construction loans?",
    "What are the pre-approval policies across lenders?",
  ],
  Borrower: [
    "Which banks accept non-resident borrowers?",
    "Which lenders offer LMI waiver for medical professionals?",
    "How do lenders treat prior credit impairment?",
  ],
};

// Shown while streaming before the first real SSE step arrives.
const FALLBACK_THINKING_STEPS = ["Thinking…"];

// ---------------------------------------------------------------------------
// PolicyAIThinkingSteps
// ---------------------------------------------------------------------------

/**
 * Thinking steps shown while the AI is working, **driven by the SSE stream**.
 * The caller pushes each real step as it arrives; the latest is in-progress and
 * earlier ones are marked done. There is NO internal timer — progress changes
 * only when a new step is added (and the list never restarts).
 */
function PolicyAIThinkingSteps({ steps }: { steps: string[] }) {
  const current = steps[steps.length - 1];
  if (!current) return null;

  return (
    <div className="flex items-center gap-2.5 px-4 py-3">
      <span className="flex shrink-0 items-center gap-0.5" aria-label="In progress">
        {[0, 1, 2].map((j) => (
          <span
            key={j}
            className="size-1 bg-muted-foreground/70 animate-bounce"
            style={{ animationDelay: `${j * 150}ms` }}
          />
        ))}
      </span>
      <span
        key={current}
        className="animate-in fade-in text-sm font-medium leading-snug text-foreground duration-300"
      >
        {current}
      </span>
    </div>
  );
}

// ---------------------------------------------------------------------------
// ResponseCard — dispatches to the correct response molecule
// ---------------------------------------------------------------------------

function ResponseCard({
  content,
  queryContext,
}: {
  content: PolicyResponseContent;
  queryContext?: PolicyQueryContext;
}) {
  if (content.type === "single_bank") {
    return (
      <PolicySingleBankAnswer
        bankName={content.bankName}
        verdict={content.verdict}
        answer={content.answer}
        citations={content.citations}
        queryContext={queryContext}
      />
    );
  }
  if (content.type === "cross_bank_comparison") {
    return (
      <PolicyComparisonTable
        categories={content.categories}
        banks={content.banks}
        summaryCounts={content.summaryCounts}
        citations={content.citations}
        queryContext={queryContext}
      />
    );
  }
  if (content.type === "ranked_list") {
    return (
      <PolicyRankedList
        categories={content.categories}
        banks={content.banks}
        citations={content.citations}
        queryContext={queryContext}
      />
    );
  }
  return null;
}

// ---------------------------------------------------------------------------
// PolicyAIFAB (Molecule)
// ---------------------------------------------------------------------------

/**
 * Floating action button that opens the PolicyAIPanel.
 *
 * @example
 * <PolicyAIFAB onClick={() => setOpen(true)} />
 */
export function PolicyAIFAB({
  onClick,
  hasNudge,
  className,
}: PolicyAIFABProps) {
  return (
    <div className={cn("relative inline-flex shrink-0", className)}>
      <Button
        data-slot="policy-ai-fab"
        size="icon"
        onClick={onClick}
        className="size-12 shadow-lg rounded-full"
        aria-label="Open Policy AI"
      >
        <BrainCircuit className="size-5" aria-hidden="true" />
      </Button>
      {hasNudge && (
        <span className="absolute -top-1 -right-1 size-3 bg-destructive rounded-full border-2 border-background" />
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// PolicyAIPanel (Organism) — Jira Rovo-style floating / inline widget
// ---------------------------------------------------------------------------

/**
 * Policy AI chat panel — shows suggested questions in home state and
 * structured response cards (SingleBankAnswer / ComparisonTable / RankedList)
 * in chat state.
 *
 * Float mode: fixed-position overlay next to the FAB.
 * Inline mode: fills its parent container — use inside drawers or tab panels.
 *
 * @example
 * <PolicyAIFAB onClick={() => setOpen(true)} />
 * <PolicyAIPanel
 *   open={open}
 *   onClose={() => setOpen(false)}
 *   messages={messages}
 *   onSendMessage={handleSend}
 *   onReset={() => setMessages([])}
 * />
 */
export function PolicyAIPanel({
  open = true,
  onClose,
  messages = [],
  suggestedQuestions,
  isStreaming = false,
  isLoading = false,
  thinkingSteps,
  onSendMessage,
  onAttachFile,
  onAttachImage,
  onReset,
  hasMoreMessages,
  isLoadingMoreMessages,
  onLoadMoreMessages,
  position = "bottom-right",
  inline = false,
  className,
}: PolicyAIPanelProps) {
  // Steps shown while streaming: the real SSE steps once they arrive, else a
  // single generic placeholder. Driven entirely by `thinkingSteps` (no timer).
  const streamingSteps =
    thinkingSteps && thinkingSteps.length > 0
      ? thinkingSteps
      : FALLBACK_THINKING_STEPS;
  const [inputValue, setInputValue] = React.useState("");
  const [minimised, setMinimised] = React.useState(false);
  const [activeTab, setActiveTab] = React.useState<PolicyTypeTab>("Income");
  const scrollRef = React.useRef<HTMLDivElement>(null);
  // Captures scrollHeight just before older messages prepend, to restore offset.
  const preLoadScrollHeightRef = React.useRef<number | null>(null);
  const prevLastMessageIdRef = React.useRef<string | undefined>(undefined);

  const isChatMode = messages.length > 0;

  // Scroll near the top → load older messages (cursor pagination).
  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
    if (!hasMoreMessages || isLoadingMoreMessages || !onLoadMoreMessages) return;
    if (e.currentTarget.scrollTop <= 80) {
      preLoadScrollHeightRef.current = e.currentTarget.scrollHeight;
      onLoadMoreMessages();
    }
  };

  // Append (new message / open) → pin to bottom. Prepend (older history just
  // loaded, tail unchanged) → restore offset so the reader stays anchored.
  React.useLayoutEffect(() => {
    const el = scrollRef.current;
    if (!el || minimised || !open) return;

    if (preLoadScrollHeightRef.current !== null) {
      el.scrollTop = el.scrollHeight - preLoadScrollHeightRef.current;
      preLoadScrollHeightRef.current = null;
      prevLastMessageIdRef.current = messages[messages.length - 1]?.id;
      return;
    }

    const currentLastId = messages[messages.length - 1]?.id;
    if (prevLastMessageIdRef.current !== currentLastId) {
      el.scrollTop = el.scrollHeight;
    }
    prevLastMessageIdRef.current = currentLastId;
  }, [messages, open, minimised]);

  const handleSend = React.useCallback(
    (text: string) => {
      if (!text || isStreaming) return;
      onSendMessage?.(text);
      setInputValue("");
    },
    [isStreaming, onSendMessage],
  );

  if (!open) return null;

  const positionClass = position === "bottom-left" ? "left-6" : "right-6";

  return (
    <div
      data-slot="policy-ai-panel"
      className={cn(
        "flex flex-col bg-card",
        inline
          ? "h-full border-0"
          : [
              "fixed z-50 border border-border shadow-2xl",
              "bottom-20 w-[400px]",
              "animate-in slide-in-from-bottom-4 fade-in duration-200",
              positionClass,
              minimised && "shadow-lg",
            ],
        className,
      )}
      style={
        inline
          ? undefined
          : {
              maxHeight: minimised ? "auto" : "min(600px, calc(100vh - 120px))",
            }
      }
    >
      {/* ── Header — float mode only ── */}
      {!inline && (
        <div className="shrink-0 flex items-center gap-2.5 px-3 py-2.5 border-b border-border bg-card">
          <BrainCircuit
            className="size-4 text-primary shrink-0"
            aria-hidden="true"
          />
          <span className="text-sm font-semibold text-foreground flex-1 truncate">
            Policy AI
          </span>

          <div className="flex items-center gap-0.5">
            {isChatMode && onReset && !minimised && (
              <Button
                variant="ghost"
                size="icon-sm"
                onClick={onReset}
                title="New conversation"
                aria-label="New conversation"
              >
                <RotateCcw className="size-3.5" aria-hidden="true" />
              </Button>
            )}
            <Button
              variant="ghost"
              size="icon-sm"
              onClick={() => setMinimised((v) => !v)}
              title={minimised ? "Restore" : "Minimise"}
              aria-label={minimised ? "Restore panel" : "Minimise panel"}
            >
              {minimised ? (
                <ChevronDown className="size-3.5" aria-hidden="true" />
              ) : (
                <Minus className="size-3.5" aria-hidden="true" />
              )}
            </Button>
            <Button
              variant="ghost"
              size="icon-sm"
              onClick={onClose}
              title="Close"
              aria-label="Close Policy AI"
            >
              <X className="size-3.5" aria-hidden="true" />
            </Button>
          </div>
        </div>
      )}

      {/* ── Body ── */}
      {(!minimised || inline) && (
        <>
          {/* Scroll container — always flex-1 + overflow-y-auto so the footer
              stays pinned at the bottom in both float and inline modes. */}
          <div
            ref={scrollRef}
            onScroll={handleScroll}
            className="flex-1 overflow-y-auto min-h-0"
          >
            {isLoading ? (
              /* Loading a saved conversation's messages — a neutral spinner,
                 NOT the thinking-steps (which are only for in-flight AI replies). */
              <div className="flex flex-1 items-center justify-center py-20">
                <div className="flex flex-col items-center gap-3">
                  <Spinner size="lg" className="text-muted-foreground" />
                  <p className="text-sm text-muted-foreground">Loading…</p>
                </div>
              </div>
            ) : !isChatMode ? (
              /* Home state — suggested questions by policy type */
              <div className="flex flex-col gap-3 px-3 py-3">
                {/* Description */}
                <p className="text-sm text-muted-foreground">
                  Ask me about lending policies across 40+ Australian banks —
                  income, LVR, security types, serviceability, and more.
                </p>

                {/* Suggested questions — wrapped in a Card for visual separation */}
                <div className="flex flex-col gap-1.5">
                  <p className="text-overline text-muted-foreground">
                    Suggested
                  </p>
                  <Card className="gap-0 py-0 shadow-none overflow-hidden">
                    {/* Policy type tabs */}
                    <div className="border-b border-border px-3 pt-2">
                      <Tabs
                        value={activeTab}
                        onValueChange={(v) => setActiveTab(v as PolicyTypeTab)}
                      >
                        <TabsList
                          variant="line"
                          className="justify-start h-8 gap-0 -mb-px overflow-x-auto"
                        >
                          {(
                            Object.keys(DEFAULT_SUGGESTED) as PolicyTypeTab[]
                          ).map((type) => (
                            <TabsTrigger
                              key={type}
                              value={type}
                              className="px-2 h-7 shrink-0"
                            >
                              {type}
                            </TabsTrigger>
                          ))}
                        </TabsList>
                      </Tabs>
                    </div>

                    {/* Question list */}
                    <div className="divide-y divide-border">
                      {(
                        (suggestedQuestions ?? DEFAULT_SUGGESTED)[activeTab] ??
                        []
                      ).map((q) => (
                        <button
                          key={q}
                          onClick={() => setInputValue(q)}
                          className="w-full text-left text-sm text-foreground px-3 py-2.5 hover:bg-muted/60 transition-colors"
                        >
                          {q}
                        </button>
                      ))}
                    </div>
                  </Card>
                </div>
              </div>
            ) : (
              /* Chat state — messages + structured response cards */
              <div className="flex flex-col gap-4 px-3 py-3">
                {isLoadingMoreMessages && (
                  <div className="flex justify-center py-1">
                    <Spinner size="sm" className="text-muted-foreground" />
                  </div>
                )}
                {messages.map((msg) =>
                  msg.role === "user" ? (
                    <ChatWidgetMessage
                      key={msg.id}
                      role="user"
                      content={msg.content}
                    />
                  ) : (
                    <div key={msg.id} className="flex flex-col gap-1.5">
                      {msg.content && (
                        <p className="text-sm text-foreground leading-relaxed">
                          {msg.content}
                        </p>
                      )}
                      {msg.responseContent && (
                        <ResponseCard
                          content={msg.responseContent}
                          queryContext={msg.queryContext}
                        />
                      )}
                    </div>
                  ),
                )}

                {isStreaming && <PolicyAIThinkingSteps steps={streamingSteps} />}
              </div>
            )}
          </div>

          {/* Footer input — hidden during loading.
              shrink-0 pins it to the flex column bottom in both modes. */}
          {!isLoading && (
            <div className="shrink-0 border-t border-border px-3 py-3 bg-card">
              <ChatInputArea
                value={inputValue}
                onChange={setInputValue}
                onSend={handleSend}
                onAttachFile={onAttachFile}
                onAttachImage={onAttachImage}
                disabled={isStreaming}
                placeholder="Ask about lending policies…"
                autoFocus={!minimised}
                showMarkdownToolbar
              />
            </div>
          )}
        </>
      )}
    </div>
  );
}
