/**
 * Ticket detail / edit overlay (F3, F4, F5, F7).
 *
 * A full-screen dialog mirroring the ticket: editable title/description, status
 * and priority, the ticket-scoped Markdown **carnet** (replaceable save, F4),
 * links to other tickets (F5) and agent assignments limited to known agents
 * (F5). Optimistic-concurrency conflicts surface a clear banner and reload
 * (F3). `#ref`s are clickable throughout (F7), and a one-click "delegation
 * context" prefill lets the user hand an agent "work on #N" (F7).
 */

import { useEffect, useState } from "react";

import type { TicketLinkKind } from "@/domain";
import { Button, Input, Spinner, cn } from "@/shared";
import { useTicketDetail } from "./useTicketDetail";
import { useProjectAgents } from "./useProjectAgents";
import {
  PriorityBadge,
  StatusBadge,
  TICKET_LINK_KINDS,
  TICKET_PRIORITIES,
  TICKET_STATUSES,
  TicketRef,
  linkKindLabel,
  linkifyTicketRefs,
  priorityLabel,
  statusLabel,
} from "./ticketMeta";

const selectClass = cn(
  "h-9 rounded-md bg-raised px-3 text-sm text-content",
  "border border-border outline-none transition-colors",
  "focus:border-primary disabled:cursor-not-allowed disabled:opacity-50",
);

const textareaClass = cn(
  "w-full rounded-md bg-raised p-3 text-sm text-content",
  "border border-border outline-none transition-colors",
  "focus:border-primary disabled:cursor-not-allowed disabled:opacity-50",
);

export interface TicketDetailProps {
  projectId: string;
  ticketRef: string;
  onClose: () => void;
  /** Navigate to another ticket (from a link or an inline `#ref`, F7). */
  onOpenRef: (ref: string) => void;
}

async function copyToClipboard(text: string): Promise<boolean> {
  if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
    await navigator.clipboard.writeText(text);
    return true;
  }
  return false;
}

function Section({
  title,
  hint,
  children,
}: {
  title: string;
  hint?: string;
  children: React.ReactNode;
}) {
  return (
    <section className="flex flex-col gap-2 border-b border-border px-5 py-4">
      <div>
        <h4 className="text-xs font-semibold uppercase tracking-wide text-muted">
          {title}
        </h4>
        {hint && <p className="mt-0.5 text-xs text-muted">{hint}</p>}
      </div>
      {children}
    </section>
  );
}

export function TicketDetail({
  projectId,
  ticketRef,
  onClose,
  onOpenRef,
}: TicketDetailProps) {
  const vm = useTicketDetail(projectId, ticketRef);
  const { agents, nameOf } = useProjectAgents(projectId);
  const t = vm.ticket;

  // Local draft state, re-seeded whenever the loaded ticket version changes so a
  // conflict-reload or an external edit refreshes the fields.
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");
  const [carnet, setCarnet] = useState("");
  const [linkTarget, setLinkTarget] = useState("");
  const [linkKind, setLinkKind] = useState<TicketLinkKind>("relatesTo");
  const [assignPick, setAssignPick] = useState("");
  const [copied, setCopied] = useState(false);

  const version = t?.version;
  useEffect(() => {
    if (!t) return;
    setTitle(t.title);
    setDescription(t.description);
    setCarnet(t.carnet ?? "");
  }, [t, version]);

  const dirtyFields =
    !!t && (title !== t.title || description !== t.description);
  const dirtyCarnet = !!t && carnet !== (t.carnet ?? "");
  const assignable = agents.filter((a) => !t?.assignedAgentIds.includes(a.id));

  async function copyDelegation() {
    if (!t) return;
    const ok = await copyToClipboard(
      `Travaille sur le ticket ${t.ref} : ${t.title}`,
    );
    if (ok) {
      setCopied(true);
      window.setTimeout(() => setCopied(false), 1200);
    }
  }

  return (
    <div
      className="fixed inset-0 z-50 flex flex-col bg-canvas"
      role="dialog"
      aria-modal="true"
      aria-label={`ticket ${ticketRef}`}
    >
      {/* ── Header ── */}
      <header className="flex shrink-0 items-center justify-between gap-3 border-b border-border px-5 py-3">
        <div className="flex min-w-0 items-center gap-2">
          <TicketRef ticketRef={ticketRef} />
          {t && <StatusBadge status={t.status} />}
          {t && <PriorityBadge priority={t.priority} />}
          <span className="truncate text-sm font-medium text-content">
            {t?.title ?? ticketRef}
          </span>
        </div>
        <div className="flex shrink-0 items-center gap-2">
          <Button
            size="sm"
            variant="ghost"
            disabled={!t}
            onClick={() => void copyDelegation()}
            title="Copy a delegation prompt to hand this ticket to an agent"
          >
            {copied ? "Copied" : "Delegation context"}
          </Button>
          <Button size="sm" variant="ghost" onClick={onClose}>
            Close
          </Button>
        </div>
      </header>

      {vm.conflict && (
        <p
          role="alert"
          className="shrink-0 border-b border-warning/40 bg-warning/10 px-5 py-2 text-sm text-warning"
        >
          This ticket was modified elsewhere and reloaded. Re-apply your change.
        </p>
      )}
      {vm.error && !vm.conflict && (
        <p
          role="alert"
          className="shrink-0 border-b border-danger/40 bg-danger/10 px-5 py-2 text-sm text-danger"
        >
          {vm.error}
        </p>
      )}

      {vm.busy && !t ? (
        <div className="flex flex-1 items-center gap-2 p-5 text-sm text-muted">
          <Spinner size={14} />
          <span>Loading ticket…</span>
        </div>
      ) : !t ? (
        <div className="flex flex-1 items-center justify-center text-sm text-muted">
          Ticket not found.
        </div>
      ) : (
        <div className="flex-1 overflow-auto">
          {/* ── Fields (F3) ── */}
          <Section title="Details">
            <label className="text-xs font-medium text-muted" htmlFor="td-title">
              Title
            </label>
            <Input
              id="td-title"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              disabled={vm.busy}
            />
            <label
              className="mt-2 text-xs font-medium text-muted"
              htmlFor="td-desc"
            >
              Description
            </label>
            <textarea
              id="td-desc"
              className={cn(textareaClass, "min-h-24")}
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              disabled={vm.busy}
            />
            {description.trim() && (
              <p className="whitespace-pre-wrap break-words rounded-md bg-raised/40 px-3 py-2 text-xs text-muted">
                {linkifyTicketRefs(description, onOpenRef)}
              </p>
            )}
            <div className="mt-1 flex items-center gap-2">
              <Button
                size="sm"
                disabled={!dirtyFields || vm.busy}
                loading={vm.busy && dirtyFields}
                onClick={() =>
                  void vm.updateFields({ title, description })
                }
              >
                Save changes
              </Button>
              {dirtyFields && (
                <Button
                  size="sm"
                  variant="ghost"
                  disabled={vm.busy}
                  onClick={() => {
                    setTitle(t.title);
                    setDescription(t.description);
                  }}
                >
                  Reset
                </Button>
              )}
            </div>
          </Section>

          {/* ── Status & priority (F3) ── */}
          <Section title="Status & priority">
            <div className="flex flex-wrap items-center gap-3">
              <label className="flex items-center gap-2 text-xs text-muted">
                Status
                <select
                  aria-label="ticket status"
                  className={selectClass}
                  value={t.status}
                  disabled={vm.busy}
                  onChange={(e) =>
                    void vm.updateFields({
                      status: e.target.value as (typeof TICKET_STATUSES)[number],
                    })
                  }
                >
                  {TICKET_STATUSES.map((s) => (
                    <option key={s} value={s}>
                      {statusLabel(s)}
                    </option>
                  ))}
                </select>
              </label>
              <label className="flex items-center gap-2 text-xs text-muted">
                Priority
                <select
                  aria-label="ticket priority"
                  className={selectClass}
                  value={t.priority}
                  disabled={vm.busy}
                  onChange={(e) =>
                    void vm.updateFields({
                      priority:
                        e.target.value as (typeof TICKET_PRIORITIES)[number],
                    })
                  }
                >
                  {TICKET_PRIORITIES.map((p) => (
                    <option key={p} value={p}>
                      {priorityLabel(p)}
                    </option>
                  ))}
                </select>
              </label>
            </div>
          </Section>

          {/* ── Carnet (F4) ── */}
          <Section
            title="Carnet"
            hint="Markdown notebook scoped to this ticket only — separate from project memory. Saving replaces the whole carnet."
          >
            <textarea
              aria-label="ticket carnet"
              className={cn(textareaClass, "min-h-40 font-mono")}
              value={carnet}
              onChange={(e) => setCarnet(e.target.value)}
              disabled={vm.busy}
            />
            <div className="flex items-center gap-2">
              <Button
                size="sm"
                disabled={!dirtyCarnet || vm.busy}
                loading={vm.busy && dirtyCarnet}
                onClick={() => void vm.saveCarnet(carnet)}
              >
                Save carnet
              </Button>
              {dirtyCarnet && (
                <Button
                  size="sm"
                  variant="ghost"
                  disabled={vm.busy}
                  onClick={() => setCarnet(t.carnet ?? "")}
                >
                  Reset
                </Button>
              )}
            </div>
          </Section>

          {/* ── Links (F5) ── */}
          <Section title="Linked tickets">
            {t.links.length === 0 ? (
              <p className="text-xs text-muted">No linked tickets.</p>
            ) : (
              <ul className="flex flex-col gap-1">
                {t.links.map((l) => (
                  <li
                    key={`${l.kind}-${l.targetRef}`}
                    className="flex items-center gap-2 text-sm"
                  >
                    <span className="text-xs text-muted">
                      {linkKindLabel(l.kind)}
                    </span>
                    <TicketRef ticketRef={l.targetRef} onOpen={onOpenRef} />
                    <Button
                      size="sm"
                      variant="ghost"
                      disabled={vm.busy}
                      onClick={() => void vm.unlink(l.targetRef, l.kind)}
                    >
                      Remove
                    </Button>
                  </li>
                ))}
              </ul>
            )}
            <div className="mt-1 flex flex-wrap items-center gap-2">
              <select
                aria-label="link kind"
                className={selectClass}
                value={linkKind}
                disabled={vm.busy}
                onChange={(e) => setLinkKind(e.target.value as TicketLinkKind)}
              >
                {TICKET_LINK_KINDS.map((k) => (
                  <option key={k} value={k}>
                    {linkKindLabel(k)}
                  </option>
                ))}
              </select>
              <Input
                aria-label="link target ref"
                placeholder="#id"
                className="w-24"
                value={linkTarget}
                onChange={(e) => setLinkTarget(e.target.value)}
              />
              <Button
                size="sm"
                disabled={!linkTarget.trim() || vm.busy}
                onClick={async () => {
                  const target = linkTarget.trim().startsWith("#")
                    ? linkTarget.trim()
                    : `#${linkTarget.trim()}`;
                  const ok = await vm.link(target, linkKind);
                  if (ok) setLinkTarget("");
                }}
              >
                Add link
              </Button>
            </div>
          </Section>

          {/* ── Assignments (F5) ── */}
          <Section
            title="Assigned agents"
            hint="Only agents known to this project can be assigned."
          >
            {t.assignedAgentIds.length === 0 ? (
              <p className="text-xs text-muted">No agents assigned.</p>
            ) : (
              <ul className="flex flex-wrap gap-2">
                {t.assignedAgentIds.map((id) => (
                  <li
                    key={id}
                    className="flex items-center gap-1 rounded-full bg-raised px-2 py-0.5 text-xs text-content"
                  >
                    {nameOf(id)}
                    <button
                      type="button"
                      aria-label={`unassign ${nameOf(id)}`}
                      className="text-muted hover:text-danger disabled:opacity-50"
                      disabled={vm.busy}
                      onClick={() => void vm.assign(id, false)}
                    >
                      ×
                    </button>
                  </li>
                ))}
              </ul>
            )}
            <div className="mt-1 flex items-center gap-2">
              <select
                aria-label="assign agent"
                className={selectClass}
                value={assignPick}
                disabled={vm.busy || assignable.length === 0}
                onChange={(e) => setAssignPick(e.target.value)}
              >
                <option value="">
                  {assignable.length === 0
                    ? "No more agents"
                    : "Select an agent…"}
                </option>
                {assignable.map((a) => (
                  <option key={a.id} value={a.id}>
                    {a.name}
                  </option>
                ))}
              </select>
              <Button
                size="sm"
                disabled={!assignPick || vm.busy}
                onClick={async () => {
                  const ok = await vm.assign(assignPick, true);
                  if (ok) setAssignPick("");
                }}
              >
                Assign
              </Button>
            </div>
          </Section>
        </div>
      )}
    </div>
  );
}
