"use client";

import { forwardRef } from "react";
import { Archive, ArchiveRestore, Ban, Mail, MailOpen, Paperclip, RefreshCw, Star, StarOff, X } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { ActiveFilters, FilterCombobox, FilterToggle, SearchInput } from "@/components/app/filters";
import { cn } from "@/lib/utils";
import { EmptyState, ErrorState } from "../components/ui/page-states";
import { useDebouncedSearch } from "../lib/useDebouncedSearch";
import { folderLabel } from "./mail-folders";
import { formatMailTime, LinkChipSummary, PartyBadge, PersonAvatar, personLabel } from "./mail-shared";

// Middle pane: search + filters, the conversation rows, bulk actions and "Load more".

const EMPTY_COPY = {
  inbox: { title: "Inbox zero", detail: "New buyer and factory mail lands here. Replies to what you send are threaded automatically." },
  unlinked: { title: "Everything is linked", detail: "Mail we could not match to a buyer, factory or order shows here, so nothing slips past the order records." },
  starred: { title: "No starred conversations", detail: "Star a conversation (press S) to keep it handy here." },
  sent: { title: "Nothing sent yet", detail: "Email you send from the ERP or your mail app appears here once synced." },
  drafts: { title: "No drafts", detail: "Save a message as a draft from the compose window to finish it later." },
  outbox: { title: "Outbox is clear", detail: "Messages waiting to send, or that failed, show here with a retry button." },
  archived: { title: "Nothing archived", detail: "Archive a conversation (press E) to clear it from the inbox without deleting it." },
  all: { title: "No email yet", detail: "Every conversation of this mailbox shows here: received, sent, archived and mail marked as not business mail." },
};

const STATUS_BADGES = {
  failed: { label: "Failed", variant: "danger" },
  queued: { label: "Queued", variant: "warning" },
  sending: { label: "Sending", variant: "info" },
  draft: { label: "Draft", variant: "neutral" },
};

// Who a row is "from": the other side of the conversation (never our own mailbox address).
function counterpart(thread, ownAddresses) {
  const others = (thread.participants || []).filter((person) => !ownAddresses.has((person.email || "").toLowerCase()));
  return others.length ? others : thread.participants || [];
}

function rowPeopleText(people) {
  const names = people.map((person) => (people.length > 1 ? person.name?.split(" ")[0] || person.email : personLabel(person)));
  return names.length > 2 ? `${names.slice(0, 2).join(", ")} +${names.length - 2}` : names.join(", ");
}

export function ThreadListPane({ url, list, summary, selection, activeId, onOpen, actions, partyOptions, canAct, rowRefs, className }) {
  const { state } = url;
  const [search, setSearch] = useDebouncedSearch(state.q, (q) => url.update({ q }));
  const ownAddresses = new Set((summary?.accounts || []).map((account) => (account.emailAddress || "").toLowerCase()));
  const selectedIds = [...selection.selected];
  const allSelected = list.items.length > 0 && list.items.every((item) => selection.selected.has(item.id));
  const someSelected = selectedIds.length > 0;
  const chips = [
    state.buyer && { key: "buyer", label: "Buyer", value: partyOptions.buyerOptions.find((option) => option.value === state.buyer)?.label || state.buyer },
    state.factory && { key: "factory", label: "Factory", value: partyOptions.factoryOptions.find((option) => option.value === state.factory)?.label || state.factory },
    state.record && { key: "record", label: "Record", value: state.record.split(":").slice(1).join(":") || state.record },
    state.unread && { key: "unread", label: "Show", value: "Unread only" },
    state.attachments && { key: "attachments", label: "Show", value: "With attachments" },
  ].filter(Boolean);
  const filtered = url.hasFilters;
  const selectedThreads = list.items.filter((item) => selection.selected.has(item.id));
  const anyUnread = selectedThreads.some((item) => item.unreadCount > 0);
  const allStarred = selectedThreads.length > 0 && selectedThreads.every((item) => item.isStarred);
  const runBulk = (action) => actions.bulk(selectedIds, action).then((ok) => { if (ok && ["archive", "unarchive", "ignore"].includes(action)) selection.clear(); });
  const empty = EMPTY_COPY[state.folder] || EMPTY_COPY.inbox;

  return <section aria-label={`${folderLabel(state.folder)} conversations`} className={cn("flex min-h-0 flex-col", className)}>
    <div className="grid gap-2 border-b px-3 pb-3 pt-3">
      <div className="flex items-center justify-between gap-2">
        <h2 className="flex min-w-0 items-baseline gap-2 text-base font-semibold">
          <span className="truncate">{folderLabel(state.folder)}</span>
          {list.hasLoaded && <span className="text-xs font-normal tabular-nums text-muted-foreground">{list.total} {list.total === 1 ? "conversation" : "conversations"}</span>}
        </h2>
        <Tooltip>
          <TooltipTrigger asChild><Button variant="ghost" size="icon-sm" aria-label="Refresh conversations" onClick={list.refresh} disabled={list.isRefreshing}>{list.isRefreshing ? <Spinner /> : <RefreshCw />}</Button></TooltipTrigger>
          <TooltipContent>Refresh</TooltipContent>
        </Tooltip>
      </div>
      <SearchInput value={search} onChange={setSearch} placeholder="Search mail…" className="sm:w-full" />
      <div className="flex flex-wrap items-center gap-2">
        {partyOptions.buyerOptions.length > 0 && <FilterCombobox label="Buyer" value={state.buyer} onChange={(buyer) => url.update({ buyer })} options={partyOptions.buyerOptions} allLabel="All" searchPlaceholder="Search buyers…" className="h-8 min-w-0 flex-1 basis-32" />}
        {partyOptions.factoryOptions.length > 0 && <FilterCombobox label="Factory" value={state.factory} onChange={(factory) => url.update({ factory })} options={partyOptions.factoryOptions} allLabel="All" searchPlaceholder="Search factories…" className="h-8 min-w-0 flex-1 basis-32" />}
        <FilterToggle pressed={Boolean(state.unread)} onPressedChange={(on) => url.update({ unread: on ? "1" : "" })}><Mail /> Unread</FilterToggle>
        <FilterToggle pressed={Boolean(state.attachments)} onPressedChange={(on) => url.update({ attachments: on ? "1" : "" })}><Paperclip /><span className="sr-only sm:not-sr-only">Files</span></FilterToggle>
      </div>
      <ActiveFilters chips={chips} onRemove={(key) => url.update({ [key]: "" })} onClear={url.clearFilters} />
    </div>

    {list.items.length > 0 && canAct && <div className={cn("flex min-h-11 flex-wrap items-center gap-1 border-b px-3 py-1.5", someSelected && "bg-muted/50")}>
      <Checkbox
        checked={allSelected ? true : someSelected ? "indeterminate" : false}
        onCheckedChange={(on) => (on && !allSelected ? selection.selectAll(list.items.map((item) => item.id)) : selection.clear())}
        aria-label={allSelected ? "Clear selection" : "Select all loaded conversations"}
        className="mr-1"
      />
      {!someSelected && <span className="text-xs text-muted-foreground">Select to act on several <span className="hidden xl:inline">(X)</span></span>}
      {someSelected && <>
        <span className="mr-1 text-xs font-medium tabular-nums">{selectedIds.length} selected</span>
        <BulkButton label={anyUnread ? "Mark read" : "Mark unread"} icon={anyUnread ? MailOpen : Mail} onClick={() => runBulk(anyUnread ? "read" : "unread")} />
        <BulkButton label={allStarred ? "Remove star" : "Star"} icon={allStarred ? StarOff : Star} onClick={() => runBulk(allStarred ? "unstar" : "star")} />
        {state.folder === "archived"
          ? <BulkButton label="Move to inbox" icon={ArchiveRestore} onClick={() => runBulk("unarchive")} />
          : <BulkButton label="Archive" icon={Archive} onClick={() => runBulk("archive")} />}
        <BulkButton label="Not business mail" icon={Ban} onClick={() => runBulk("ignore")} />
        <Button variant="ghost" size="icon-sm" className="ml-auto" aria-label="Clear selection" onClick={selection.clear}><X /></Button>
      </>}
    </div>}

    <div className={cn("min-h-0 flex-1 overflow-y-auto transition-opacity", list.isRefreshing && "opacity-60")} aria-busy={list.isRefreshing || list.isFirstLoad}>
      {list.isFirstLoad && <ThreadListSkeleton />}
      {!list.isFirstLoad && list.error && <div className="p-3"><ErrorState compact error={list.error} title="Conversations could not be loaded" onRetry={list.refresh} /></div>}
      {!list.isFirstLoad && !list.error && !list.items.length && <div className="p-3">
        <EmptyState compact filtered={filtered} onClearFilters={url.clearFilters}
          title={filtered ? `No conversations in ${folderLabel(state.folder).toLowerCase()} match` : empty.title}
          detail={filtered ? "Try other words, or clear the filters." : empty.detail} />
      </div>}
      {!list.error && list.items.length > 0 && <ul className="divide-y">
        {list.items.map((thread) => <ThreadRow
          key={thread.id}
          ref={(element) => { if (element) rowRefs.current.set(thread.id, element); else rowRefs.current.delete(thread.id); }}
          thread={thread}
          people={counterpart(thread, ownAddresses)}
          outgoing={["sent", "drafts", "outbox"].includes(state.folder)}
          active={thread.id === activeId}
          selected={selection.selected.has(thread.id)}
          selectionMode={someSelected}
          canAct={canAct}
          onOpen={() => onOpen(thread)}
          onToggleSelect={() => selection.toggle(thread.id)}
          onToggleStar={() => actions.bulk([thread.id], thread.isStarred ? "unstar" : "star")}
        />)}
      </ul>}
      {list.items.length > 0 && <div className="grid justify-items-center gap-1 px-3 py-4 text-xs text-muted-foreground">
        {list.hasMore
          ? <Button variant="outline" size="sm" onClick={list.loadMore} disabled={list.isLoadingMore}>{list.isLoadingMore && <Spinner />} Load more</Button>
          : list.total > 10 && <span>That is everything in {folderLabel(state.folder).toLowerCase()}.</span>}
        {list.hasMore && <span className="tabular-nums">Showing {list.items.length} of {list.total}</span>}
      </div>}
    </div>
  </section>;
}

function BulkButton({ label, icon: Icon, onClick }) {
  return <Tooltip>
    <TooltipTrigger asChild><Button variant="ghost" size="icon-sm" aria-label={label} onClick={onClick}><Icon /></Button></TooltipTrigger>
    <TooltipContent>{label}</TooltipContent>
  </Tooltip>;
}

const ThreadRow = forwardRef(function ThreadRow({ thread, people, outgoing, active, selected, selectionMode, canAct, onOpen, onToggleSelect, onToggleStar }, ref) {
  const unread = thread.unreadCount > 0;
  const party = people.find((person) => person.party)?.party;
  const status = STATUS_BADGES[thread.latestStatus];
  const who = rowPeopleText(people);

  return <li className={cn("group/row relative flex gap-2.5 px-3 py-2.5 transition-colors hover:bg-accent/60", active && "bg-accent", selected && "bg-primary/5")}>
    {unread && <span className="absolute inset-y-2 left-0 w-0.5 rounded-full bg-primary" aria-hidden="true" />}
    <div className="relative size-8 shrink-0">
      <PersonAvatar person={people[0]} className={cn("transition-opacity", canAct && (selectionMode || selected ? "opacity-0" : "group-hover/row:opacity-0 group-focus-within/row:opacity-0 pointer-coarse:opacity-0"))} />
      {canAct && <span className={cn("absolute inset-0 grid place-items-center transition-opacity", selectionMode || selected ? "opacity-100" : "opacity-0 group-hover/row:opacity-100 group-focus-within/row:opacity-100 pointer-coarse:opacity-100")}>
        <Checkbox checked={selected} onCheckedChange={onToggleSelect} aria-label={`Select conversation: ${thread.subject || "(no subject)"}`} />
      </span>}
    </div>
    <button
      ref={ref}
      type="button"
      onClick={onOpen}
      aria-current={active ? "true" : undefined}
      data-thread-id={thread.id}
      className="grid min-w-0 flex-1 gap-0.5 rounded-sm text-left outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
    >
      <span className="flex min-w-0 items-baseline gap-2">
        <span className={cn("min-w-0 flex-1 truncate text-sm", unread ? "font-semibold text-foreground" : "text-foreground/85")}>
          {outgoing && <span className="font-normal text-muted-foreground">To: </span>}{who}
          {thread.messageCount > 1 && <span className="ml-1 text-xs font-normal tabular-nums text-muted-foreground">{thread.messageCount}</span>}
        </span>
        <time dateTime={thread.lastMessageAt} className={cn("shrink-0 text-xs tabular-nums", unread ? "font-medium text-foreground" : "text-muted-foreground")}>{formatMailTime(thread.lastMessageAt)}</time>
      </span>
      <span className="flex min-w-0 items-center gap-1.5">
        <span className={cn("min-w-0 flex-1 truncate text-sm", unread ? "font-semibold" : "text-foreground/85")}>{thread.subject || "(no subject)"}</span>
        {thread.hasAttachments && <Paperclip className="size-3.5 shrink-0 text-muted-foreground" aria-label="Has attachments" />}
        {thread.needsReply && <span className="flex shrink-0 items-center" title="Needs reply: the last message is from outside"><span className="size-2 rounded-full bg-warning" aria-hidden="true" /><span className="sr-only">Needs reply</span></span>}
      </span>
      <span className="line-clamp-1 text-xs text-muted-foreground">{thread.snippet}</span>
      {(party || thread.links?.length > 0 || status || thread.linkState === "unlinked") && <span className="mt-1 flex min-w-0 flex-wrap items-center gap-1">
        {status && <Badge variant={status.variant} className="h-5 px-1.5">{status.label}</Badge>}
        {party && <PartyBadge party={party} />}
        <LinkChipSummary links={thread.links} className="min-w-0" />
        {thread.linkState === "unlinked" && !thread.links?.length && <Badge variant="warning" className="h-5 px-1.5 font-normal">Not linked</Badge>}
      </span>}
    </button>
    {canAct && <Button
      variant="ghost"
      size="icon-xs"
      className={cn("shrink-0 self-start text-muted-foreground", thread.isStarred ? "text-warning" : "opacity-0 group-hover/row:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100")}
      aria-label={thread.isStarred ? "Remove star" : "Star conversation"}
      aria-pressed={thread.isStarred}
      onClick={onToggleStar}
    ><Star className={cn(thread.isStarred && "fill-current")} /></Button>}
  </li>;
});

function ThreadListSkeleton() {
  return <div role="status" aria-label="Loading conversations" className="divide-y">
    {Array.from({ length: 8 }).map((_, index) => <div key={index} className="flex gap-2.5 px-3 py-3">
      <Skeleton className="size-8 rounded-full" />
      <div className="grid flex-1 gap-1.5">
        <div className="flex justify-between gap-4"><Skeleton className="h-4 w-1/3" /><Skeleton className="h-3 w-12" /></div>
        <Skeleton className="h-4 w-4/5" />
        <Skeleton className="h-3 w-full" />
      </div>
    </div>)}
  </div>;
}
