import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Send } from "lucide-react";
import { api, type Message, type MessageUser } from "../lib/api";
import { useLiveQuery } from "../lib/useLiveQuery";
import { relativeTime } from "../lib/format";
import { partnerPub } from "../lib/messaging";
import * as e2ee from "../lib/e2ee";
import KeyGate from "./KeyGate";

const POLL_MS = 20000; // safety net; live updates arrive over the WebSocket

// The message list + composer for one conversation, shared by the full Messages page and the
// floating dock window. Handles client-side decryption and encrypt-on-send; the surrounding
// chrome (back button / minimize / close) is the caller's.
export default function ChatThread({
  meId,
  partnerId,
  isSystem,
  unlocked,
}: {
  meId: number;
  partnerId: number;
  isSystem: boolean;
  unlocked: boolean;
}) {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const [draft, setDraft] = useState("");
  const [plain, setPlain] = useState<Record<number, string>>({});
  const bottomRef = useRef<HTMLDivElement>(null);
  const needsKey = !isSystem && !unlocked;

  const q = useLiveQuery<{ partner: MessageUser; items: Message[] }>(
    ["thread", partnerId],
    () => api.thread(partnerId),
    { intervalMs: POLL_MS, enabled: !needsKey }
  );
  const items = q.data?.items ?? [];

  useEffect(() => {
    if (isSystem || !unlocked) return;
    let cancelled = false;
    (async () => {
      const out: Record<number, string> = {};
      for (const m of items) {
        if (m.ciphertext && m.iv) {
          try {
            const pub = await partnerPub(partnerId);
            out[m.id] = await e2ee.decryptFrom(partnerId, pub, m.ciphertext, m.iv);
          } catch {
            out[m.id] = "⚠ " + t("messages.cantDecrypt");
          }
        }
      }
      if (!cancelled) setPlain(out);
    })();
    return () => {
      cancelled = true;
    };
  }, [items, unlocked, isSystem, partnerId, t]);

  // Opening / refreshing the thread marks incoming messages read, so clear the badges.
  useEffect(() => {
    if (q.dataUpdatedAt) {
      qc.invalidateQueries({ queryKey: ["conversations"] });
      qc.invalidateQueries({ queryKey: ["message-unread"] });
    }
  }, [q.dataUpdatedAt, qc]);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ block: "end" });
  }, [items.length, plain]);

  const send = useMutation({
    mutationFn: async (text: string) => {
      const pub = await partnerPub(partnerId);
      const { ciphertext, iv } = await e2ee.encryptFor(partnerId, pub, text);
      return api.sendMessage(partnerId, ciphertext, iv);
    },
    onSuccess: () => {
      setDraft("");
      qc.invalidateQueries({ queryKey: ["thread", partnerId] });
      qc.invalidateQueries({ queryKey: ["conversations"] });
    },
  });

  const submit = () => {
    const text = draft.trim();
    if (text && !send.isPending) send.mutate(text);
  };

  if (needsKey) {
    return (
      <div className="flex-1 grid place-items-center p-3 overflow-y-auto">
        <div className="w-full max-w-sm">
          <KeyGate meId={meId} compact />
        </div>
      </div>
    );
  }

  return (
    <>
      <div className="flex-1 overflow-y-auto p-3 flex flex-col gap-2">
        {items.length === 0 ? (
          <div className="m-auto text-sm text-muted">{t("messages.threadEmpty")}</div>
        ) : (
          items.map((m) => {
            const mine = m.sender_id === meId;
            const text = m.kind === "system" ? m.body ?? "" : plain[m.id] ?? "…";
            return (
              <div key={m.id} className={`flex ${mine ? "justify-end" : "justify-start"}`}>
                <div
                  className={`max-w-[80%] rounded-2xl px-3 py-2 text-sm whitespace-pre-wrap break-words ${
                    mine ? "bg-accent text-accent-fg rounded-br-sm" : "bg-card rounded-bl-sm"
                  }`}
                >
                  {text}
                  <div className={`text-[10px] mt-1 ${mine ? "text-accent-fg/70" : "text-muted"}`}>
                    {relativeTime(m.created_at)}
                  </div>
                </div>
              </div>
            );
          })
        )}
        <div ref={bottomRef} />
      </div>

      {isSystem ? (
        <div className="p-2 border-t border-border text-center text-xs text-muted">{t("messages.systemReadOnly")}</div>
      ) : (
        <div className="flex items-end gap-2 p-2 border-t border-border">
          <textarea
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === "Enter" && !e.shiftKey) {
                e.preventDefault();
                submit();
              }
            }}
            rows={1}
            placeholder={t("messages.writePlaceholder")}
            className="flex-1 resize-none max-h-28 bg-card border border-border rounded-xl px-3 py-2 text-sm outline-none focus:border-accent"
          />
          <button
            onClick={submit}
            disabled={!draft.trim() || send.isPending}
            className="shrink-0 p-2.5 rounded-xl bg-accent text-accent-fg hover:opacity-90 transition disabled:opacity-40"
            aria-label={t("messages.send")}
            title={t("messages.send")}
          >
            <Send className="w-4 h-4" />
          </button>
        </div>
      )}
    </>
  );
}
