"use client";

/**
 * ChatPanel — grounded parcel-chat for the Site Finder analysis screen (#958).
 *
 * Consumes POST /api/v1/chat/ask. The thread (user/assistant turns) lives in
 * component state; the most recent turns are passed back as `history` (capped at
 * MAX_HISTORY_TURNS) so the backend keeps context. Five preset-intent quick
 * buttons send a canned question with the matching `intent`.
 *
 * Send is an ACTION → driven by a TanStack `useMutation` (no useEffect for HTTP,
 * per frontend.md). The backend answer is PLAIN TEXT and is rendered as text
 * with preserved whitespace — never injected as HTML (XSS rule).
 *
 * States:
 *   sending          → input disabled, "Отправляем…" affordance
 *   report_status==="pending" → backend's "run analysis first" message, shown as
 *                      a neutral assistant turn (not an error)
 *   network error    → inline error row with a retry button (re-runs the last ask)
 *
 * Each assistant answer surfaces: an advisory <Badge> (advisory===true), a
 * grounded_in provenance line ("источник: разделы X · run #N"), and a subtle
 * "детерминированный ответ" marker when llm_used===false.
 */

import { useRef, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { MessageSquare, Send, AlertTriangle } from "lucide-react";

import { Badge } from "@/components/ui/Badge";
import { postChatAsk } from "@/lib/site-finder-api";
import type {
  ChatAskRequest,
  ChatAskResponse,
  ChatIntent,
  ChatMessage,
} from "@/lib/site-finder-api";

// ── Props ─────────────────────────────────────────────────────────────────────

interface Props {
  cadNum: string;
}

// ── Local thread model ─────────────────────────────────────────────────────────

interface AssistantMeta {
  advisory: boolean;
  llmUsed: boolean;
  reportStatus: ChatAskResponse["report_status"];
  groundedIn: ChatAskResponse["grounded_in"];
}

interface ThreadTurn {
  id: number;
  role: "user" | "assistant";
  content: string;
  /** Present only on assistant turns. */
  meta?: AssistantMeta;
}

// ── Constants ──────────────────────────────────────────────────────────────────

/** Recent turns sent back as context (cap ~10, per the issue). */
const MAX_HISTORY_TURNS = 10;
const SCROLL_MARGIN = 72;

const PRESETS: { intent: ChatIntent; label: string; message: string }[] = [
  {
    intent: "summary",
    label: "Сводка",
    message: "Дай краткую сводку по участку.",
  },
  {
    intent: "what_to_build",
    label: "Что строить",
    message: "Что выгодно построить на этом участке?",
  },
  {
    intent: "why_forecast",
    label: "Почему такой прогноз",
    message: "Почему прогноз именно такой?",
  },
  { intent: "risks", label: "Риски", message: "Какие риски у этого участка?" },
  {
    intent: "scenarios",
    label: "Сценарии",
    message: "Какие сценарии развития возможны?",
  },
];

// ── Helpers ────────────────────────────────────────────────────────────────────

/** Map backend section codes to the provenance line, e.g. "разделы 6.1, 6.4". */
function provenanceLine(meta: AssistantMeta): string | null {
  const g = meta.groundedIn;
  if (!g) return null;
  const parts: string[] = [];
  if (g.sections.length > 0) {
    parts.push(`разделы ${g.sections.join(", ")}`);
  }
  parts.push(`run #${g.run_id.toLocaleString("ru")}`);
  return `источник: ${parts.join(" · ")}`;
}

// ── ChatPanel ──────────────────────────────────────────────────────────────────

export function ChatPanel({ cadNum }: Props) {
  const [thread, setThread] = useState<ThreadTurn[]>([]);
  const [input, setInput] = useState("");
  // Last request kept so a failed send can be retried verbatim.
  const lastRequestRef = useRef<ChatAskRequest | null>(null);
  const turnIdRef = useRef(0);

  const nextTurnId = () => {
    turnIdRef.current += 1;
    return turnIdRef.current;
  };

  const mutation = useMutation<ChatAskResponse, Error, ChatAskRequest>({
    mutationFn: postChatAsk,
    onSuccess: (res) => {
      setThread((prev) => [
        ...prev,
        {
          id: nextTurnId(),
          role: "assistant",
          content: res.answer,
          meta: {
            advisory: res.advisory,
            llmUsed: res.llm_used,
            reportStatus: res.report_status,
            groundedIn: res.grounded_in,
          },
        },
      ]);
    },
  });

  /** Build history from the thread (recent turns only). */
  function buildHistory(): ChatMessage[] {
    return thread
      .slice(-MAX_HISTORY_TURNS)
      .map((t) => ({ role: t.role, content: t.content }));
  }

  /** Append a user turn and fire the ask. */
  function send(message: string, intent?: ChatIntent) {
    const trimmed = message.trim();
    if (trimmed === "" || mutation.isPending) return;

    const history = buildHistory();
    setThread((prev) => [
      ...prev,
      { id: nextTurnId(), role: "user", content: trimmed },
    ]);

    const req: ChatAskRequest = {
      cad_num: cadNum,
      message: trimmed,
      ...(intent ? { intent } : {}),
      ...(history.length > 0 ? { history } : {}),
    };
    lastRequestRef.current = req;
    mutation.mutate(req);
  }

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    const value = input;
    setInput("");
    send(value);
  }

  function handleRetry() {
    const req = lastRequestRef.current;
    if (req) mutation.mutate(req);
  }

  const isEmpty = thread.length === 0;

  return (
    <section
      id="chat-panel"
      aria-label="Чат по участку"
      style={{
        scrollMarginTop: SCROLL_MARGIN,
        background: "var(--bg-card)",
        border: "1px solid var(--border-card)",
        borderRadius: 12,
        padding: 20,
      }}
    >
      {/* Header */}
      <header style={{ marginBottom: 12 }}>
        <h2
          style={{
            margin: 0,
            fontSize: 18,
            fontWeight: 600,
            color: "var(--fg-primary)",
            display: "flex",
            alignItems: "center",
            gap: 8,
          }}
        >
          <MessageSquare size={20} strokeWidth={1.5} aria-hidden />
          Чат по участку
        </h2>
        <p
          style={{
            margin: "4px 0 0",
            fontSize: 13,
            color: "var(--fg-secondary)",
          }}
        >
          Вопросы по анализу участка. Ответы опираются на рассчитанный прогноз.
        </p>
      </header>

      {/* Preset-intent quick buttons */}
      <div
        style={{
          display: "flex",
          flexWrap: "wrap",
          gap: 8,
          marginBottom: 16,
        }}
      >
        {PRESETS.map((p) => (
          <button
            key={p.intent}
            type="button"
            onClick={() => send(p.message, p.intent)}
            disabled={mutation.isPending}
            style={{
              padding: "6px 12px",
              fontSize: 13,
              fontWeight: 500,
              color: "var(--accent)",
              background: "var(--accent-soft)",
              border: "1px solid transparent",
              borderRadius: 6,
              cursor: mutation.isPending ? "default" : "pointer",
              opacity: mutation.isPending ? 0.6 : 1,
            }}
          >
            {p.label}
          </button>
        ))}
      </div>

      {/* Message thread */}
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          gap: 12,
          marginBottom: 16,
        }}
      >
        {isEmpty && !mutation.isPending && (
          <p
            style={{
              margin: 0,
              padding: "16px 0",
              fontSize: 13,
              color: "var(--fg-tertiary)",
            }}
          >
            Задайте вопрос или выберите тему выше.
          </p>
        )}

        {thread.map((turn) =>
          turn.role === "user" ? (
            <UserBubble key={turn.id} content={turn.content} />
          ) : (
            <AssistantBubble
              key={turn.id}
              content={turn.content}
              meta={turn.meta}
            />
          ),
        )}

        {mutation.isPending && (
          <p
            style={{
              margin: 0,
              fontSize: 13,
              color: "var(--fg-tertiary)",
            }}
          >
            Отправляем…
          </p>
        )}

        {mutation.isError && (
          <div
            role="alert"
            style={{
              display: "flex",
              alignItems: "center",
              flexWrap: "wrap",
              gap: 12,
              padding: "12px 16px",
              background: "var(--danger-soft)",
              border: "1px solid var(--danger)",
              borderRadius: 12,
              fontSize: 13,
              color: "var(--danger)",
            }}
          >
            <span
              style={{ display: "inline-flex", alignItems: "center", gap: 6 }}
            >
              <AlertTriangle size={16} strokeWidth={1.5} aria-hidden />
              Не удалось отправить запрос. Проверьте соединение.
            </span>
            <button
              type="button"
              onClick={handleRetry}
              style={{
                padding: "4px 12px",
                fontSize: 13,
                fontWeight: 500,
                color: "var(--danger)",
                background: "var(--bg-card)",
                border: "1px solid var(--danger)",
                borderRadius: 6,
                cursor: "pointer",
              }}
            >
              Повторить
            </button>
          </div>
        )}
      </div>

      {/* Composer */}
      <form
        onSubmit={handleSubmit}
        style={{ display: "flex", alignItems: "flex-end", gap: 8 }}
      >
        <label htmlFor="chat-input" style={visuallyHidden}>
          Сообщение
        </label>
        <textarea
          id="chat-input"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter" && !e.shiftKey) {
              e.preventDefault();
              const value = input;
              setInput("");
              send(value);
            }
          }}
          rows={2}
          placeholder="Спросите про участок…"
          disabled={mutation.isPending}
          style={{
            flex: 1,
            resize: "vertical",
            minHeight: 44,
            padding: "8px 12px",
            fontSize: 14,
            lineHeight: 1.4,
            color: "var(--fg-primary)",
            background: "var(--bg-card)",
            border: "1px solid var(--border-strong)",
            borderRadius: 8,
            fontFamily: "inherit",
          }}
        />
        <button
          type="submit"
          aria-label="Отправить сообщение"
          disabled={mutation.isPending || input.trim() === ""}
          style={{
            display: "inline-flex",
            alignItems: "center",
            gap: 6,
            padding: "10px 16px",
            fontSize: 14,
            fontWeight: 500,
            color: "var(--fg-on-dark)",
            background: "var(--accent)",
            border: "1px solid transparent",
            borderRadius: 8,
            cursor:
              mutation.isPending || input.trim() === "" ? "default" : "pointer",
            opacity: mutation.isPending || input.trim() === "" ? 0.6 : 1,
            whiteSpace: "nowrap",
          }}
        >
          <Send size={16} strokeWidth={1.5} aria-hidden />
          Отправить
        </button>
      </form>
    </section>
  );
}

// ── Bubbles ────────────────────────────────────────────────────────────────────

const visuallyHidden: React.CSSProperties = {
  position: "absolute",
  width: 1,
  height: 1,
  padding: 0,
  margin: -1,
  overflow: "hidden",
  clip: "rect(0 0 0 0)",
  whiteSpace: "nowrap",
  border: 0,
};

function UserBubble({ content }: { content: string }) {
  return (
    <div style={{ display: "flex", justifyContent: "flex-end" }}>
      <div
        style={{
          maxWidth: "85%",
          padding: "10px 14px",
          background: "var(--accent-soft)",
          border: "1px solid transparent",
          borderRadius: 12,
          fontSize: 14,
          lineHeight: 1.5,
          color: "var(--fg-primary)",
          // Plain text: preserve the user's line breaks without HTML injection.
          whiteSpace: "pre-wrap",
          wordBreak: "break-word",
        }}
      >
        {content}
      </div>
    </div>
  );
}

function AssistantBubble({
  content,
  meta,
}: {
  content: string;
  meta?: AssistantMeta;
}) {
  const isPending = meta?.reportStatus === "pending";
  const provenance = meta ? provenanceLine(meta) : null;

  return (
    <div style={{ display: "flex", justifyContent: "flex-start" }}>
      <div
        style={{
          maxWidth: "85%",
          padding: "10px 14px",
          background: "var(--bg-card-alt)",
          border: `1px solid ${
            isPending ? "var(--warn)" : "var(--border-card)"
          }`,
          borderRadius: 12,
        }}
      >
        {/* Badge row */}
        {meta && (meta.advisory || isPending || !meta.llmUsed) && (
          <div
            style={{
              display: "flex",
              flexWrap: "wrap",
              gap: 6,
              marginBottom: 8,
            }}
          >
            {isPending && (
              <Badge variant="warning" size="sm">
                Прогноз не рассчитан
              </Badge>
            )}
            {meta.advisory && (
              <Badge
                variant="warning"
                size="sm"
                icon={<AlertTriangle size={12} strokeWidth={1.5} />}
              >
                Advisory
              </Badge>
            )}
            {!meta.llmUsed && (
              <Badge variant="neutral" size="sm">
                Детерминированный ответ
              </Badge>
            )}
          </div>
        )}

        {/* Answer — PLAIN TEXT (backend returns plain text; no HTML injection). */}
        <div
          style={{
            fontSize: 14,
            lineHeight: 1.5,
            color: "var(--fg-primary)",
            whiteSpace: "pre-wrap",
            wordBreak: "break-word",
          }}
        >
          {content}
        </div>

        {/* grounded_in provenance line */}
        {provenance && (
          <p
            style={{
              margin: "8px 0 0",
              fontSize: 12,
              lineHeight: 1.5,
              color: "var(--fg-tertiary)",
              fontVariantNumeric: "tabular-nums",
            }}
          >
            {provenance}
          </p>
        )}
      </div>
    </div>
  );
}
