"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Image from "next/image";
import { LayoutGrid, Power, WifiOff, X } from "lucide-react";
import { toast } from "sonner";
import { useDraggableFab } from "@/lib/hooks/useDraggableFab";
import { useFabClearance } from "@/lib/hooks/useFabClearance";
import { useOnlineStatus } from "@/lib/hooks/useOnlineStatus";
import { CHAT_COPY, LANGUAGE_META, type LanguageCode } from "@/lib/chat/i18n";
import { useChat } from "@/lib/chat/useChat";
import { cn } from "@/lib/utils";
import { ChatBlockProvider, type ChatBlockApi } from "./ChatBlockContext";
import Composer from "./Composer";
import LanguagePicker from "./LanguagePicker";
import MessageList from "./MessageList";

const PHONE_QUERY = "(max-width: 639px)";

function isPhoneViewport(): boolean {
  return typeof window !== "undefined" && window.matchMedia(PHONE_QUERY).matches;
}

type Props = {
  /**
   * The dashboard has a bottom tab bar below `lg`; lift the sm+ corner button
   * above it so it doesn't cover the last tab. (Phones use the edge ball.)
   */
  aboveTabBar?: boolean;
};

/**
 * Biznie AI — the floating assistant.
 *
 * This file is the shell only: the FAB (draggable on phones), the panel (a
 * full-screen sheet under `sm`, a floating card above), focus handling and
 * scroll lock. Conversation state lives in `useChat`; what the bot says is
 * decided server-side and rendered block by block (`./blocks`).
 */
export default function BiznieAiChatbot({ aboveTabBar = false }: Props) {
  const [open, setOpen] = useState(false);
  /** The user asked to change language (the picker also shows when none is stored). */
  const [picking, setPicking] = useState(false);
  /** The "End this chat?" confirmation is showing. */
  const [confirmingEnd, setConfirmingEnd] = useState(false);
  const online = useOnlineStatus();

  const fabRef = useRef<HTMLButtonElement>(null);
  const panelRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const returnFocusRef = useRef(false);
  const keepChattingRef = useRef<HTMLButtonElement>(null);

  // useChat only bootstraps once a language is set, so "open and not picking"
  // is exactly "the conversation is on screen".
  const chat = useChat({ active: open && !picking });
  const showPicker = !chat.language || picking;

  const displayLanguage: LanguageCode = chat.language ?? "en";
  const copy = CHAT_COPY[displayLanguage];

  // Drag-to-reposition is a phone affordance only; on larger screens the button
  // keeps its fixed corner and the hook stays inert.
  const [isPhone, setIsPhone] = useState(false);
  useEffect(() => {
    const query = window.matchMedia(PHONE_QUERY);
    const sync = () => setIsPhone(query.matches);
    sync();
    query.addEventListener("change", sync);
    return () => query.removeEventListener("change", sync);
  }, []);

  const fab = useDraggableFab("biznie-ai-fab-position", isPhone && !open);
  // Off the footer links and out of the way of a form being filled (B03).
  // Phones keep their dragged position, so only sm+ is lifted.
  const clearance = useFabClearance(!isPhone && !open, panelRef);

  const close = useCallback((restoreFocus = true) => {
    returnFocusRef.current = restoreFocus;
    setOpen(false);
    setPicking(false);
    setConfirmingEnd(false);
  }, []);

  // The panel is a full-screen sheet under `sm`, so lock the page behind it —
  // otherwise iOS scrolls the document when a swipe hits the sheet's edge.
  useEffect(() => {
    if (!open || !isPhoneViewport()) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = prev;
    };
  }, [open]);

  // Esc closes from anywhere inside the page (widgets that use Esc themselves
  // stop propagation first). With the end-chat confirmation up, Esc only
  // dismisses that.
  useEffect(() => {
    if (!open) return;
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key !== "Escape" || event.defaultPrevented) return;
      if (confirmingEnd) setConfirmingEnd(false);
      else close(true);
    };
    document.addEventListener("keydown", onKeyDown);
    return () => document.removeEventListener("keydown", onKeyDown);
  }, [open, close, confirmingEnd]);

  // The safe choice gets focus, so a stray Enter doesn't wipe the chat.
  useEffect(() => {
    if (confirmingEnd) keepChattingRef.current?.focus();
  }, [confirmingEnd]);

  // Move focus into the panel on open; back to the FAB on close. Acts only on
  // the open/close transition, not on later picker toggles.
  const wasOpenRef = useRef(false);
  useEffect(() => {
    const wasOpen = wasOpenRef.current;
    wasOpenRef.current = open;
    if (open && !wasOpen) {
      // Phones: don't pop the keyboard over the greeting — focus the panel itself.
      if (!showPicker && !isPhoneViewport()) inputRef.current?.focus();
      else panelRef.current?.focus();
    } else if (!open && wasOpen && returnFocusRef.current) {
      returnFocusRef.current = false;
      fabRef.current?.focus();
    }
  }, [open, showPicker]);

  const onNavigate = useCallback(() => {
    if (isPhoneViewport()) close(false);
  }, [close]);

  const endChat = () => {
    chat.endChat();
    toast.success(copy.chatEnded);
    close(true);
  };

  const handleLanguageSelect = (language: LanguageCode) => {
    chat.setLanguage(language);
    setPicking(false);
  };

  const cooling = chat.cooldownLeft > 0;
  const busy = !!chat.pending || cooling || !online;

  const blockApi = useMemo<ChatBlockApi>(
    () => ({
      copy,
      language: displayLanguage,
      busy,
      pendingFormKey: chat.pending?.formKey ?? null,
      onNavigate,
      sendText: chat.sendText,
      sendAction: chat.sendAction,
      retry: chat.retry,
      isFormSubmitted: chat.isFormSubmitted,
      isFormDismissed: chat.isFormDismissed,
      dismissForm: chat.dismissForm,
      prepareResume: chat.prepareResume,
      cancelResume: chat.cancelResume,
    }),
    [
      copy,
      displayLanguage,
      busy,
      chat.pending,
      onNavigate,
      chat.sendText,
      chat.sendAction,
      chat.retry,
      chat.isFormSubmitted,
      chat.isFormDismissed,
      chat.dismissForm,
      chat.prepareResume,
      chat.cancelResume,
    ]
  );

  // Unread badge: bot replies that arrived while the panel was closed.
  const botCount = useMemo(() => chat.entries.filter((e) => e.role === "bot").length, [chat.entries]);
  const [seenBotCount, setSeenBotCount] = useState<number | null>(null);
  useEffect(() => {
    if (!chat.hydrated) return;
    if (open || seenBotCount === null || botCount < seenBotCount) setSeenBotCount(botCount);
  }, [open, botCount, seenBotCount, chat.hydrated]);
  const unread = open || seenBotCount === null ? 0 : botCount - seenBotCount;

  return (
    /* Phones: a draggable quick-ball docked to either edge (see useDraggableFab;
       the inline style carries the user's saved position and overrides the
       classes below). sm+: the conventional bottom-right corner. */
    <div
      style={
        !open && clearance.lift > 0
          ? { ...fab.style, transform: `translateY(-${clearance.lift}px)` }
          : fab.style
      }
      className={cn(
        "fixed right-1.5 top-[62%] z-[70] transition-[transform,opacity] duration-200 sm:bottom-6 sm:right-6 sm:top-auto",
        aboveTabBar && "sm:bottom-24 lg:bottom-6",
        !open && clearance.hidden && "pointer-events-none translate-y-2 opacity-0"
      )}
      aria-hidden={!open && clearance.hidden ? true : undefined}
    >
      {open ? (
        /* Mobile: a real full-screen sheet (dvh so the Safari toolbar can't clip
           the composer). sm+: reverts to the floating card anchored to the FAB. */
        <div
          ref={panelRef}
          role="dialog"
          aria-label="Biznie AI"
          tabIndex={-1}
          className="fixed inset-0 z-[80] flex h-[100dvh] flex-col overflow-hidden bg-white outline-none sm:absolute sm:inset-auto sm:bottom-0 sm:right-0 sm:h-auto sm:w-[400px] sm:rounded-2xl sm:border sm:border-red-100 sm:shadow-2xl"
        >
          <div className="flex shrink-0 items-center justify-between bg-gradient-to-r from-[#CC1414] to-[#A50E0E] px-4 pb-3 pt-[calc(0.75rem+env(safe-area-inset-top))] text-white sm:pt-3">
            <div className="flex items-center gap-2">
              <span className="relative inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/95 shadow-[0_0_0_1px_rgba(255,255,255,0.22),0_6px_14px_rgba(0,0,0,0.14)]">
                <span className="pointer-events-none absolute inset-0 rounded-full bg-gradient-to-br from-white/80 via-white/30 to-transparent" />
                <Image src="/favicon.png" alt="" width={20} height={20} className="relative h-5 w-5 rounded-full" />
              </span>
              <div>
                <p className="text-sm font-semibold">Biznie AI</p>
                <p className="text-[11px] text-red-100">{copy.assistantStatus}</p>
              </div>
            </div>
            <div className="flex items-center gap-1.5">
              {!showPicker ? (
                <button
                  type="button"
                  onClick={() => void chat.sendAction({ type: "menu" }, { echo: copy.mainMenu })}
                  disabled={busy}
                  className="rounded-md p-1.5 text-white/90 transition-colors hover:bg-white/10 disabled:opacity-50"
                  aria-label={copy.mainMenu}
                  title={copy.mainMenu}
                >
                  <LayoutGrid size={15} aria-hidden />
                </button>
              ) : null}
              {chat.language ? (
                <button
                  type="button"
                  onClick={() => setPicking((v) => !v)}
                  aria-pressed={picking}
                  className="rounded-md bg-white/15 px-2 py-1 text-[10px] font-semibold text-white transition-colors hover:bg-white/20"
                  aria-label={`${copy.changeLanguage} (${LANGUAGE_META[chat.language].label})`}
                >
                  {LANGUAGE_META[chat.language].nativeLabel}
                </button>
              ) : null}
              {!showPicker && chat.entries.length > 0 ? (
                <button
                  type="button"
                  onClick={() => setConfirmingEnd(true)}
                  aria-pressed={confirmingEnd}
                  className="rounded-md p-1.5 text-white/90 transition-colors hover:bg-white/10"
                  aria-label={copy.endChat}
                  title={copy.endChat}
                >
                  <Power size={15} aria-hidden />
                </button>
              ) : null}
              <button
                type="button"
                onClick={() => close(true)}
                className="rounded-md p-1.5 text-white/90 transition-colors hover:bg-white/10"
                aria-label={copy.closeChat}
                title={copy.closeChat}
              >
                <X size={16} aria-hidden />
              </button>
            </div>
          </div>


          {showPicker ? (
            <LanguagePicker displayLanguage={displayLanguage} current={chat.language} onSelect={handleLanguageSelect} />
          ) : (
            <ChatBlockProvider value={blockApi}>
              {!online ? (
                <div
                  role="status"
                  className="flex shrink-0 items-center gap-2 bg-amber-500 px-3 py-1.5 text-[11px] font-medium text-white"
                >
                  <WifiOff size={13} aria-hidden /> {copy.offline}
                </div>
              ) : null}
              <MessageList entries={chat.entries} pending={!!chat.pending} onRetry={(send) => void chat.retry(send)} />
              <Composer
                language={displayLanguage}
                copy={copy}
                disabled={busy}
                offline={!online}
                cooldownLeft={chat.cooldownLeft}
                onSend={(text) => void chat.sendText(text)}
                inputRef={inputRef}
              />
            </ChatBlockProvider>
          )}

          {/* An overlay, not an inline bar: the panel's height is fixed on sm+
              (anchored to the FAB), so anything added to the flow pushes the
              header off the top of the screen. */}
          {confirmingEnd && !showPicker ? (
            <div
              className="absolute inset-0 z-20 flex items-end justify-center bg-gray-900/40 p-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] sm:items-center sm:rounded-2xl sm:pb-3"
              onClick={(event) => {
                if (event.target === event.currentTarget) setConfirmingEnd(false);
              }}
            >
              <div
                role="alertdialog"
                aria-modal="true"
                aria-labelledby="biznie-ai-end-title"
                aria-describedby="biznie-ai-end-body"
                className="w-full max-w-sm rounded-2xl bg-white p-4 shadow-2xl ring-1 ring-gray-100"
              >
                <span className="mb-2 inline-flex h-9 w-9 items-center justify-center rounded-full bg-red-50 text-[#CC1414]">
                  <Power size={16} aria-hidden />
                </span>
                <p id="biznie-ai-end-title" className="text-sm font-semibold text-gray-900">
                  {copy.endChatTitle}
                </p>
                <p id="biznie-ai-end-body" className="mt-1 text-xs leading-relaxed text-gray-600">
                  {copy.endChatBody}
                </p>
                <div className="mt-4 grid grid-cols-2 gap-2">
                  <button
                    ref={keepChattingRef}
                    type="button"
                    onClick={() => setConfirmingEnd(false)}
                    className="rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-semibold text-gray-700 transition-colors hover:bg-gray-50"
                  >
                    {copy.keepChatting}
                  </button>
                  <button
                    type="button"
                    onClick={endChat}
                    className="rounded-lg bg-[#CC1414] px-3 py-2 text-xs font-semibold text-white transition-colors hover:bg-[#A50E0E]"
                  >
                    {copy.endChat}
                  </button>
                </div>
              </div>
            </div>
          ) : null}
        </div>
      ) : null}

      {!open ? (
        <button
          ref={fabRef}
          type="button"
          onClick={() => {
            // A drag finishes with a click event; ignore that one.
            if (fab.consumeDragClick()) return;
            setOpen(true);
          }}
          {...fab.handlers}
          /* Phones: a small icon-only ball, idle at 70% opacity so it reads as an
             overlay rather than blocking content. lg+: the labelled pill. */
          className={`group relative inline-flex h-11 w-11 touch-none select-none items-center justify-center rounded-full bg-[#CC1414] text-sm font-semibold text-white shadow-lg ring-1 ring-white/40 transition-all hover:opacity-100 sm:h-12 sm:w-12 sm:opacity-100 sm:shadow-xl sm:hover:-translate-y-0.5 sm:hover:bg-[#A50E0E] ${clearance.footerInView ? "" : "lg:h-auto lg:w-auto lg:gap-2 lg:px-4 lg:py-3"} ${
            fab.dragging
              ? "scale-110 opacity-100 shadow-2xl ring-2 ring-white/70"
              : "opacity-70 active:scale-95 active:opacity-100"
          }`}
          aria-label={copy.openChat}
          aria-haspopup="dialog"
          title="Tap to chat · Hold and drag to move"
        >
          <span className="relative inline-flex h-7 w-7 items-center justify-center rounded-full bg-white/95 shadow-[0_0_0_1px_rgba(255,255,255,0.22),0_6px_14px_rgba(0,0,0,0.14)]">
            <span className="pointer-events-none absolute -inset-1 animate-pulse rounded-full opacity-70 ring-1 ring-white/30" />
            <span className="pointer-events-none absolute inset-0 rounded-full bg-gradient-to-br from-white/80 via-white/30 to-transparent" />
            <Image src="/favicon.png" alt="" width={20} height={20} className="relative h-5 w-5 rounded-full" />
          </span>
          {/* Icon only over the footer, so the pill never reaches its links. */}
          {!clearance.footerInView && <span className="hidden lg:inline">Biznie AI</span>}
          {unread > 0 ? (
            <span className="absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full bg-white px-1 text-[11px] font-bold text-[#CC1414] shadow ring-2 ring-[#CC1414] lg:static lg:px-2 lg:py-0.5 lg:shadow-none lg:ring-0">
              {unread}
            </span>
          ) : null}
        </button>
      ) : null}
    </div>
  );
}
