"use client";

import { createPortal } from "react-dom";

import { useEffect, useRef, useState } from "react";
import { Minus, X, Send, ImagePlus, Loader2, Tag, ChevronRight } from "lucide-react";
import { Avatar } from "./Avatar";
import { fileToDataUrl } from "@/lib/image";
import { sfx } from "@/lib/sfx";
import type { DirectMessage, MarketplaceListing, User } from "@/lib/types";
import { useCurrency } from "@/lib/currency";
import Link from "next/link";
import clsx from "clsx";

/** Facebook-style docked chat, so buyers never leave the listing. */
export function ChatDock({
  seller,
  subject,
  listing,
  onClose,
}: {
  seller: User;
  /** Listing title, prefilled as the opening line. */
  subject?: string;
  /** Shown as a header card so both sides know what's being discussed. */
  listing?: MarketplaceListing;
  onClose: () => void;
}) {
  const { format: money } = useCurrency();
  const [minimised, setMinimised] = useState(false);
  const [messages, setMessages] = useState<DirectMessage[]>([]);
  const [text, setText] = useState(
    subject ? `Hi, is "${subject}" still available?` : ""
  );
  const [conversationId, setConversationId] = useState<string | null>(null);
  const [sending, setSending] = useState(false);
  const listRef = useRef<HTMLDivElement>(null);
  const fileRef = useRef<HTMLInputElement>(null);
  const [rating, setRating] = useState(0);
  const [reviewCount, setReviewCount] = useState(0);

  useEffect(() => {
    fetch(`/api/marketplace/seller/${seller.username}/reviews`)
      .then((r) => (r.ok ? r.json() : null))
      .then((d) => {
        if (!d) return;
        setRating(d.average ?? 0);
        setReviewCount(d.total ?? 0);
      })
      .catch(() => {});
  }, [seller.username]);

  // Find an existing thread with this seller, if there is one.
  // Always announce the listing — this also backfills chats that existed
  // before conversations tracked which listing they concern.
  useEffect(() => {
    if (!listing?.id) return;
    fetch("/api/messages/start", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username: seller.username, listingId: listing.id }),
    }).catch(() => {});
  }, [listing?.id, seller.username]);

  useEffect(() => {
    fetch("/api/messages")
      .then((r) => (r.ok ? r.json() : []))
      .then((all) => {
        const found = Array.isArray(all)
          ? all.find((c) => !c.isGroup && c.otherUser?.username === seller.username)
          : null;
        if (found) {
          setConversationId(found.id);
          return fetch(`/api/messages/${found.id}`)
            .then((r) => (r.ok ? r.json() : null))
            .then((d) => d && setMessages(d.messages ?? []));
        }
      })
      .catch(() => {});
  }, [seller.username]);

  useEffect(() => {
    listRef.current?.scrollTo({ top: listRef.current.scrollHeight });
  }, [messages, minimised]);

  // Poll so replies appear without reopening the dock.
  useEffect(() => {
    if (!conversationId) return;
    const t = setInterval(() => {
      fetch(`/api/messages/${conversationId}`)
        .then((r) => (r.ok ? r.json() : null))
        .then((d) => d && setMessages(d.messages ?? []))
        .catch(() => {});
    }, 4000);
    return () => clearInterval(t);
  }, [conversationId]);

  async function send(imageUrl?: string) {
    const body = text.trim();
    if (!body && !imageUrl) return;
    setSending(true);

    let id = conversationId;
    if (!id) {
      const started = await fetch("/api/messages/start", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          username: seller.username,
          listingId: listing?.id,
        }),
      }).catch(() => null);
      if (started && started.ok) {
        const d = await started.json();
        id = d.id;
        setConversationId(d.id);
      }
    }
    if (!id) {
      setSending(false);
      return;
    }

    const res = await fetch(`/api/messages/${id}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: body, imageUrl }),
    }).catch(() => null);

    if (res && res.ok) {
      const msg = await res.json();
      setMessages((m) => [...m, msg]);
      setText("");
      sfx.send();
    }
    setSending(false);
  }

  return createPortal(
    <div className={clsx("cd", minimised && "min")}>
      <div className="cd-head" onClick={() => minimised && setMinimised(false)}>
        <Avatar user={seller} size={32} effect={false} />
        <span className="min-w-0 flex-1">
          <b>{seller.name}</b>
          <em>
            <span className="cd-stars">★★★★★</span>
            {rating.toFixed(1)} · {reviewCount} reviews
          </em>
        </span>
        <button onClick={(e) => { e.stopPropagation(); setMinimised((v) => !v); }} title="Minimise">
          <Minus size={15} />
        </button>
        <button onClick={(e) => { e.stopPropagation(); onClose(); }} title="Close">
          <X size={15} />
        </button>
      </div>

      {!minimised && (
        <>
          {listing && (
            <Link href={`/marketplace/${listing.id}`} className="cd-listing">
              <span className="cd-listing-img">
                {listing.imageUrl ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img src={listing.imageUrl} alt="" />
                ) : (
                  <Tag size={16} />
                )}
              </span>
              <span className="min-w-0 flex-1">
                <b>{listing.title}</b>
                <em>{money(listing.price)}</em>
              </span>
              <ChevronRight size={15} className="text-neutral-300 shrink-0" />
            </Link>
          )}

          <div className="cd-body" ref={listRef}>
            {messages.length === 0 && (
              <p className="cd-empty">
                Say hello — {seller.name.split(" ")[0]} will see this in Messages.
              </p>
            )}
            {messages.map((m) => (
              <div key={m.id} className={clsx("cd-msg", m.senderId === seller.id ? "in" : "out")}>
                {m.imageUrl ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img src={m.imageUrl} alt="" />
                ) : (
                  m.text
                )}
              </div>
            ))}
          </div>

          <div className="cd-bar">
            <input
              ref={fileRef}
              type="file"
              accept="image/*"
              className="hidden"
              onChange={async (e) => {
                const f = e.target.files?.[0];
                e.target.value = "";
                if (!f) return;
                const url = await fileToDataUrl(f, 1000).catch(() => null);
                if (url) send(url);
              }}
            />
            <button onClick={() => fileRef.current?.click()} title="Send a photo">
              <ImagePlus size={16} />
            </button>
            <input
              value={text}
              onChange={(e) => setText(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && send()}
              placeholder="Write a message..."
            />
            <button
              onClick={() => send()}
              disabled={sending || !text.trim()}
              className="cd-send"
            >
              {sending ? <Loader2 size={15} className="animate-spin" /> : <Send size={15} />}
            </button>
          </div>
        </>
      )}
    </div>,
    document.body
  );
}
