"use client";

// SupportChatPanel — the actual chat window opened by SupportButton's
// floating trigger / TopNav's "Помощь" item. Messages are mirrored to the
// same Telegram support topic as before this feature (backend
// `feat/tradein-web-chat-backend`, app/api/v1/support.py — a mirror of the
// pre-existing Telegram support bridge, backend PR #2526): the operator still
// answers from Telegram exactly as before, only the client-facing surface
// moved on-site.
//
// Modal-dialog plumbing (focus trap / Escape / restore-focus-on-close)
// mirrors LocationDrawer.tsx, with one difference: initial focus goes to the
// message textarea, not the dialog shell — visitors open this panel to type,
// not to read a landing focus target.
import { useEffect, useRef, useState } from "react";
import type { KeyboardEvent as ReactKeyboardEvent } from "react";

import { tokens } from "./tokens";
import { SUPPORT_BOT_URL } from "./SupportChatContext";
import {
  MAX_SUPPORT_MESSAGE_LENGTH,
  describeSupportError,
  useMarkSupportRead,
  useSendSupportMessage,
  useSupportMessages,
} from "@/lib/useSupportChat";
import type { SupportMessage } from "@/lib/useSupportChat";

interface SupportChatPanelProps {
  open: boolean;
  onClose: () => void;
}

const TIME_FORMAT = new Intl.DateTimeFormat("ru-RU", {
  hour: "2-digit",
  minute: "2-digit",
});

function formatTime(iso: string): string {
  const parsed = new Date(iso);
  if (Number.isNaN(parsed.getTime())) return "";
  return TIME_FORMAT.format(parsed);
}

// Show the char counter once the visitor is close enough to the limit that it
// becomes actionable — no point cluttering the footer for a 20-char message.
const COUNTER_THRESHOLD = MAX_SUPPORT_MESSAGE_LENGTH - 500;

const PANEL_STYLES = `
.support-chat-send:disabled { opacity: .45; cursor: default; }
.support-chat-send:not(:disabled):hover { background: ${tokens.accentDeep}; }
.support-chat-retry:hover { border-color: ${tokens.accent}; color: ${tokens.accent}; }
.support-chat-textarea:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(46,139,255,.3); }
.support-chat-close:hover { border-color: ${tokens.accent}; color: ${tokens.accent}; }
`;

export function SupportChatPanel({ open, onClose }: SupportChatPanelProps) {
  const dialogRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const listEndRef = useRef<HTMLDivElement>(null);
  const lastFocused = useRef<Element | null>(null);
  const onCloseRef = useRef(onClose);
  onCloseRef.current = onClose;

  const [draft, setDraft] = useState("");

  const messagesQuery = useSupportMessages(open);
  const sendMessage = useSendSupportMessage();
  const markRead = useMarkSupportRead();

  // Mark the thread read on BOTH the closed->open and open->closed edges (the
  // effect fires on mount-while-open and its cleanup fires on the reverse
  // transition/unmount) — see useMarkSupportRead docstring for why both
  // directions matter. Read via a ref so this doesn't need `markRead.mutate`
  // in the dep array (a fresh function identity every render would otherwise
  // re-fire the effect every render).
  const markReadRef = useRef(markRead.mutate);
  markReadRef.current = markRead.mutate;
  useEffect(() => {
    if (!open) return;
    markReadRef.current();
    return () => {
      markReadRef.current();
    };
  }, [open]);

  // Focus trap + Escape + initial focus into the textarea (req: focus goes to
  // the input on open, not the dialog shell). Mirrors LocationDrawer.tsx.
  useEffect(() => {
    if (!open) return;
    const dialog = dialogRef.current;
    lastFocused.current = document.activeElement;
    inputRef.current?.focus();

    function getFocusable(): HTMLElement[] {
      if (!dialog) return [];
      const nodes = dialog.querySelectorAll<HTMLElement>(
        'button, textarea, [href], input, select, [tabindex]:not([tabindex="-1"])',
      );
      return Array.from(nodes).filter((el) => !el.hasAttribute("disabled"));
    }

    function onKeyDown(e: KeyboardEvent) {
      if (e.key === "Escape") {
        e.preventDefault();
        e.stopPropagation();
        onCloseRef.current();
        return;
      }
      if (e.key !== "Tab" || !dialog) return;

      const focusable = getFocusable();
      if (focusable.length === 0) {
        e.preventDefault();
        dialog.focus();
        return;
      }

      const first = focusable[0];
      const last = focusable[focusable.length - 1];
      const activeEl = document.activeElement;
      const inside = dialog.contains(activeEl);

      if (e.shiftKey) {
        if (activeEl === first || activeEl === dialog || !inside) {
          e.preventDefault();
          last.focus();
        }
      } else if (activeEl === last || !inside) {
        e.preventDefault();
        first.focus();
      }
    }

    document.addEventListener("keydown", onKeyDown);
    return () => {
      document.removeEventListener("keydown", onKeyDown);
      (lastFocused.current as HTMLElement | null)?.focus?.();
    };
  }, [open]);

  const messages = messagesQuery.data ?? [];

  // Autoscroll to the latest message on open and whenever the transcript
  // grows (new poll result / own message appended after send).
  useEffect(() => {
    if (!open) return;
    listEndRef.current?.scrollIntoView({ block: "end" });
  }, [open, messages.length]);

  const trimmedLength = draft.trim().length;
  const overLimit = draft.length > MAX_SUPPORT_MESSAGE_LENGTH;
  const canSend = trimmedLength > 0 && !overLimit && !sendMessage.isPending;

  function handleSend() {
    if (!canSend) return;
    sendMessage.mutate(draft.trim(), {
      onSuccess: () => setDraft(""),
    });
  }

  function handleTextareaKeyDown(e: ReactKeyboardEvent<HTMLTextAreaElement>) {
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  }

  return (
    <>
      <style>{PANEL_STYLES}</style>
      <div
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-label="Чат поддержки МЕРА"
        tabIndex={-1}
        // Off-screen when closed → keep it out of the a11y tree and inert
        // (mirrors LocationDrawer.tsx — same axe "aria-hidden-focus" concern).
        aria-hidden={!open}
        inert={!open ? true : undefined}
        style={{
          position: "fixed",
          right: 20,
          bottom: 76,
          width: 360,
          height: 520,
          maxHeight: "calc(100vh - 96px)",
          // Below TopNav (50) / UserMenu (200) so it never steals their
          // clicks, but above the floating trigger button (25) and the
          // LocationDrawer scrim/panel (40/41) — the chat window should sit
          // on top of everything except the persistent top chrome.
          zIndex: 45,
          transform: open ? "translateY(0)" : "translateY(10px)",
          opacity: open ? 1 : 0,
          pointerEvents: open ? "auto" : "none",
          transition: "opacity .18s ease, transform .18s ease",
          display: "flex",
          flexDirection: "column",
          background: tokens.surface.w98,
          border: `1px solid ${tokens.line}`,
          borderRadius: 14,
          boxShadow: "0 20px 60px rgba(23,38,58,.28)",
          overflow: "hidden",
          fontFamily: tokens.font.sans,
        }}
      >
        {/* header */}
        <div
          style={{
            flex: "0 0 auto",
            display: "flex",
            alignItems: "center",
            justifyContent: "space-between",
            padding: "12px 14px",
            borderBottom: `1px solid ${tokens.lineSoft}`,
            background: tokens.surface.w70,
          }}
        >
          <div
            style={{
              fontSize: 13,
              fontWeight: 600,
              color: tokens.ink2,
              letterSpacing: ".2px",
            }}
          >
            Поддержка МЕРА
          </div>
          <button
            type="button"
            className="support-chat-close"
            onClick={onClose}
            aria-label="Закрыть чат поддержки"
            style={{
              width: 28,
              height: 28,
              flex: "0 0 auto",
              padding: 0,
              border: `1px solid ${tokens.line}`,
              borderRadius: 6,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              cursor: "pointer",
              color: tokens.muted,
              background: tokens.surface.w60,
              fontFamily: "inherit",
              fontSize: 14,
              lineHeight: 1,
              transition: "all .15s",
            }}
          >
            ✕
          </button>
        </div>

        {/* message list */}
        <div
          style={{
            flex: "1 1 auto",
            overflowY: "auto",
            padding: "12px 14px",
            display: "flex",
            flexDirection: "column",
            gap: 8,
          }}
        >
          {messagesQuery.isLoading ? (
            <div
              style={{ fontSize: 12, color: tokens.muted3, textAlign: "center", marginTop: 24 }}
            >
              Загрузка сообщений…
            </div>
          ) : messagesQuery.isError ? (
            <div
              style={{
                fontSize: 12,
                color: tokens.body2,
                background: tokens.infoSoftBg,
                border: `1px solid ${tokens.infoSoftBorder}`,
                borderRadius: 8,
                padding: "10px 12px",
                display: "flex",
                flexDirection: "column",
                gap: 8,
              }}
            >
              <span>{describeSupportError(messagesQuery.error)}</span>
              <button
                type="button"
                className="support-chat-retry"
                onClick={() => messagesQuery.refetch()}
                style={{
                  alignSelf: "flex-start",
                  border: `1px solid ${tokens.line}`,
                  borderRadius: 6,
                  padding: "4px 10px",
                  fontSize: 11,
                  color: tokens.body2,
                  background: tokens.surface.w80,
                  cursor: "pointer",
                  fontFamily: "inherit",
                  transition: "all .15s",
                }}
              >
                Повторить
              </button>
            </div>
          ) : messages.length === 0 ? (
            <div
              style={{
                fontSize: 12.5,
                lineHeight: 1.6,
                color: tokens.body2,
                background: tokens.infoSoftBg,
                border: `1px solid ${tokens.infoSoftBorder}`,
                borderRadius: 8,
                padding: "12px 14px",
              }}
            >
              Здравствуйте! Опишите вопрос — оператор отвечает в рабочее время,
              обычно в течение часа.
            </div>
          ) : (
            messages.map((m) => <SupportMessageBubble key={m.id} message={m} />)
          )}
          <div ref={listEndRef} />
        </div>

        {/* send-error banner */}
        {sendMessage.isError && (
          <div
            style={{
              flex: "0 0 auto",
              margin: "0 14px 8px",
              fontSize: 11.5,
              color: tokens.danger,
              background: "rgba(205,104,104,.08)",
              border: "1px solid rgba(205,104,104,.3)",
              borderRadius: 7,
              padding: "7px 10px",
            }}
          >
            {describeSupportError(sendMessage.error)}
          </div>
        )}

        {/* composer */}
        <div
          style={{
            flex: "0 0 auto",
            borderTop: `1px solid ${tokens.lineSoft}`,
            padding: "10px 12px",
            background: tokens.surface.w80,
          }}
        >
          <textarea
            ref={inputRef}
            className="support-chat-textarea"
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            onKeyDown={handleTextareaKeyDown}
            placeholder="Напишите сообщение…"
            aria-label="Сообщение в поддержку"
            rows={2}
            style={{
              width: "100%",
              resize: "none",
              boxSizing: "border-box",
              border: `1px solid ${overLimit ? tokens.danger : tokens.line}`,
              borderRadius: 8,
              padding: "8px 10px",
              fontFamily: "inherit",
              fontSize: 12.5,
              color: tokens.ink2,
              background: tokens.surface.w98,
            }}
          />
          <div
            style={{
              marginTop: 8,
              display: "flex",
              alignItems: "center",
              justifyContent: "space-between",
              gap: 8,
            }}
          >
            <a
              href={SUPPORT_BOT_URL}
              target="_blank"
              rel="noopener noreferrer"
              style={{
                fontSize: 10.5,
                color: tokens.muted3,
                textDecoration: "underline",
              }}
            >
              написать в Telegram
            </a>
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              {draft.length > COUNTER_THRESHOLD && (
                <span
                  style={{
                    fontSize: 10,
                    fontFamily: tokens.font.mono,
                    color: overLimit ? tokens.danger : tokens.muted3,
                  }}
                >
                  {draft.length}/{MAX_SUPPORT_MESSAGE_LENGTH}
                </span>
              )}
              <button
                type="button"
                className="support-chat-send"
                onClick={handleSend}
                disabled={!canSend}
                aria-label="Отправить сообщение"
                style={{
                  border: "none",
                  borderRadius: 7,
                  padding: "7px 16px",
                  fontSize: 12,
                  fontWeight: 600,
                  color: tokens.onAccent,
                  background: tokens.accent,
                  cursor: canSend ? "pointer" : "default",
                  fontFamily: "inherit",
                  transition: "background .15s",
                }}
              >
                Отправить
              </button>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}

function SupportMessageBubble({ message }: { message: SupportMessage }) {
  const isOwn = message.direction === "in";
  return (
    <div style={{ display: "flex", justifyContent: isOwn ? "flex-end" : "flex-start" }}>
      <div
        style={{
          maxWidth: "78%",
          background: isOwn ? tokens.accent : tokens.surface.w80,
          color: isOwn ? tokens.onAccent : tokens.ink2,
          border: isOwn ? "none" : `1px solid ${tokens.lineSoft}`,
          borderRadius: 10,
          padding: "8px 10px",
        }}
      >
        {/* Plain text node — operator replies are untrusted input, never
            rendered as HTML. `pre-wrap` preserves newlines/whitespace without
            needing dangerouslySetInnerHTML. */}
        <div style={{ fontSize: 12.5, lineHeight: 1.5, whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
          {message.text_body}
        </div>
        <div
          style={{
            marginTop: 4,
            fontSize: 9,
            opacity: 0.75,
            textAlign: isOwn ? "right" : "left",
          }}
        >
          {formatTime(message.created_at)}
        </div>
      </div>
    </div>
  );
}
