/**
 * Sprint management overlay (ticket #11, F3).
 *
 * A full-screen dialog to create, rename, reorder (accessible up/down buttons,
 * no drag-and-drop required) and delete sprints, plus add/remove tickets to a
 * sprint. All behaviour comes from {@link useTickets} via the injected view-model
 * — this component is pure presentation over the same hook instance the panel
 * uses, so mutations reflect live.
 *
 * Deleting a sprint UNASSIGNS its tickets (they fall back to the backlog); it
 * never deletes tickets — the confirmation says so explicitly.
 *
 * Ticket view (#37): the sprint tickets are read through this component's OWN
 * {@link useTicketSearch} instance — independent of the main Tickets panel
 * filter — so tickets stay visible once closed (no default `statuses` filter)
 * and can be filtered/sorted per status/priority via the shared
 * {@link TicketFacetsBar}. Rows show their {@link StatusBadge}/{@link
 * PriorityBadge}. Grouping by sprint is done client-side on the returned rows.
 */

import { useState } from "react";

import { Button, Input } from "@/shared";
import { PriorityBadge, StatusBadge, TicketRef } from "./ticketMeta";
import { TicketFacetsBar } from "./TicketFacetsBar";
import { TicketPicker } from "./TicketPicker";
import { useTicketSearch } from "./useTicketSearch";
import type { TicketsViewModel } from "./useTickets";

export interface SprintManagerProps {
  projectId: string;
  vm: TicketsViewModel;
  onClose: () => void;
}

export function SprintManager({ projectId, vm, onClose }: SprintManagerProps) {
  // Own ticket query, independent of the main panel filter (#37). No default
  // `statuses` → closed/done tickets remain visible; the facets bar below drives
  // per-status/priority filtering + sort of the whole sprint view.
  const search = useTicketSearch(projectId, { refreshOnEvents: true });
  const items = search.rows;
  const sprints = [...vm.sprints].sort((a, b) => a.order - b.order);

  const [newName, setNewName] = useState("");
  // Local rename drafts keyed by sprint id; falls back to the stored name.
  const [renameDrafts, setRenameDrafts] = useState<Record<string, string>>({});
  // Sprint pending delete confirmation, or null.
  const [confirmDelete, setConfirmDelete] = useState<
    { id: string; name: string } | null
  >(null);
  // The sprint whose "add ticket" TicketPicker is open, or null (#19). Held as
  // {id,name} so the picker keeps rendering while the underlying list refreshes.
  const [pickerSprint, setPickerSprint] = useState<
    { id: string; name: string } | null
  >(null);

  async function handleCreate(e: React.FormEvent) {
    e.preventDefault();
    // Name is optional in the UI; default to a positional name (the backend
    // requires a non-empty name).
    const name = newName.trim() || `Sprint ${sprints.length + 1}`;
    const created = await vm.createSprint(name);
    if (created) setNewName("");
  }

  return (
    <div
      className="fixed inset-0 z-50 flex flex-col bg-canvas"
      role="dialog"
      aria-modal="true"
      aria-label="manage sprints"
    >
      {/* ── Header ── */}
      <header className="flex shrink-0 items-center justify-between gap-3 border-b border-border px-5 py-3">
        <span className="text-sm font-medium text-content">Manage sprints</span>
        <Button size="sm" variant="ghost" onClick={onClose}>
          Close
        </Button>
      </header>

      {vm.error && (
        <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>
      )}

      <div className="flex-1 overflow-auto p-5">
        {/* ── Create ── */}
        <form
          onSubmit={handleCreate}
          className="mb-5 flex items-center gap-2 rounded-md border border-border bg-raised/50 p-2"
        >
          <Input
            aria-label="new sprint name"
            placeholder="Sprint name (optional)"
            value={newName}
            onChange={(e) => setNewName(e.target.value)}
            className="max-w-xs"
          />
          <Button type="submit" size="sm" disabled={vm.busy}>
            Create sprint
          </Button>
        </form>

        {/* ── Sprint-ticket filters (#37) — independent of the main panel;
            filters/sorts every sprint's tickets by status/priority/text. ── */}
        <div className="mb-5">
          <TicketFacetsBar
            text={search.text}
            onTextChange={search.setText}
            statuses={search.statuses}
            priorities={search.priorities}
            onToggleStatus={search.toggleStatus}
            onTogglePriority={search.togglePriority}
            onClearFacets={search.clearFacets}
            sort={search.sort}
            onSortChange={search.setSort}
          />
        </div>

        {search.error && (
          <p
            role="alert"
            className="mb-4 rounded-md border border-danger/40 bg-danger/10 px-3 py-2 text-sm text-danger"
          >
            {search.error}
          </p>
        )}

        {sprints.length === 0 ? (
          <p className="text-sm text-muted">No sprints yet.</p>
        ) : (
          <ul className="flex flex-col gap-4">
            {sprints.map((sprint, index) => {
              const inSprint = items.filter((t) => t.sprintId === sprint.id);
              const draft = renameDrafts[sprint.id] ?? sprint.name;
              return (
                <li
                  key={sprint.id}
                  aria-label={`sprint row ${sprint.name}`}
                  className="rounded-md border border-border p-3"
                >
                  {/* ── Row header: order, rename, reorder, delete ── */}
                  <div className="flex flex-wrap items-center gap-2">
                    <span className="text-xs font-semibold text-muted">
                      #{sprint.order}
                    </span>
                    <Input
                      aria-label={`rename sprint ${sprint.name}`}
                      value={draft}
                      disabled={vm.busy}
                      onChange={(e) =>
                        setRenameDrafts((prev) => ({
                          ...prev,
                          [sprint.id]: e.target.value,
                        }))
                      }
                      className="max-w-xs"
                    />
                    <Button
                      size="sm"
                      aria-label={`save sprint ${sprint.name}`}
                      disabled={
                        vm.busy ||
                        draft.trim() === "" ||
                        draft.trim() === sprint.name
                      }
                      onClick={async () => {
                        const ok = await vm.renameSprint(
                          sprint.id,
                          draft.trim(),
                        );
                        if (ok) {
                          setRenameDrafts((prev) => {
                            const { [sprint.id]: _drop, ...rest } = prev;
                            return rest;
                          });
                        }
                      }}
                    >
                      Save
                    </Button>
                    <div className="ml-auto flex items-center gap-1">
                      <Button
                        size="sm"
                        variant="ghost"
                        aria-label={`move sprint ${sprint.name} up`}
                        disabled={vm.busy || index === 0}
                        onClick={() => void vm.moveSprint(sprint.id, "up")}
                      >
                        ↑
                      </Button>
                      <Button
                        size="sm"
                        variant="ghost"
                        aria-label={`move sprint ${sprint.name} down`}
                        disabled={vm.busy || index === sprints.length - 1}
                        onClick={() => void vm.moveSprint(sprint.id, "down")}
                      >
                        ↓
                      </Button>
                      <Button
                        size="sm"
                        variant="ghost"
                        aria-label={`delete sprint ${sprint.name}`}
                        disabled={vm.busy}
                        className="text-danger hover:text-danger"
                        onClick={() =>
                          setConfirmDelete({ id: sprint.id, name: sprint.name })
                        }
                      >
                        Delete
                      </Button>
                    </div>
                  </div>

                  {/* ── Tickets in this sprint (with status/priority badges,
                      #37; kept visible even once closed/done) ── */}
                  <div className="mt-3 flex flex-col gap-1">
                    {inSprint.length === 0 ? (
                      <p className="text-xs text-muted">No tickets in this sprint.</p>
                    ) : (
                      <ul className="flex flex-col gap-1">
                        {inSprint.map((t) => (
                          <li
                            key={t.ref}
                            className="flex items-center gap-2 text-sm"
                          >
                            <TicketRef ticketRef={t.ref} />
                            <span className="min-w-0 flex-1 truncate text-content">
                              {t.title}
                            </span>
                            <span className="flex shrink-0 items-center gap-1">
                              <StatusBadge status={t.status} />
                              <PriorityBadge priority={t.priority} />
                            </span>
                            <Button
                              size="sm"
                              variant="ghost"
                              aria-label={`remove ${t.ref} from sprint`}
                              disabled={vm.busy}
                              onClick={() => void vm.assignSprint(t.ref, null)}
                            >
                              Remove
                            </Button>
                          </li>
                        ))}
                      </ul>
                    )}

                    {/* Add existing tickets via the TicketPicker popup (#19/#41):
                        multi-select, excluding tickets already in this sprint. */}
                    <div className="mt-1">
                      <Button
                        size="sm"
                        variant="ghost"
                        aria-label={`add ticket to sprint ${sprint.name}`}
                        disabled={vm.busy}
                        onClick={() =>
                          setPickerSprint({ id: sprint.id, name: sprint.name })
                        }
                      >
                        Ajouter des tickets
                      </Button>
                    </div>
                  </div>
                </li>
              );
            })}
          </ul>
        )}

        {/* More sprint tickets may exist beyond the first page (#37). */}
        {search.hasMore && (
          <div className="mt-4 flex justify-center">
            <Button
              size="sm"
              variant="ghost"
              loading={search.busy}
              onClick={() => search.loadMore()}
            >
              Load more tickets
            </Button>
          </div>
        )}
      </div>

      {/* ── Delete confirmation ── */}
      {confirmDelete && (
        <div
          role="dialog"
          aria-modal="true"
          aria-label="delete sprint confirmation"
          className="fixed inset-0 z-[60] flex items-center justify-center bg-black/55 p-4"
        >
          <div className="w-full max-w-sm rounded-lg border border-border bg-surface p-5 shadow-xl">
            <h4 className="text-sm font-semibold text-content">
              Supprimer le sprint « {confirmDelete.name} »&nbsp;?
            </h4>
            <p className="mt-2 text-sm text-muted">
              Les tickets de ce sprint ne seront pas supprimés&nbsp;: ils seront
              simplement désassignés et retourneront au backlog «&nbsp;Sans
              sprint&nbsp;».
            </p>
            <div className="mt-4 flex items-center justify-end gap-2">
              <Button
                size="sm"
                variant="ghost"
                aria-label="cancel delete sprint"
                disabled={vm.busy}
                onClick={() => setConfirmDelete(null)}
              >
                Annuler
              </Button>
              <Button
                size="sm"
                aria-label="confirm delete sprint"
                className="text-danger hover:text-danger"
                disabled={vm.busy}
                onClick={async () => {
                  await vm.deleteSprint(confirmDelete.id);
                  setConfirmDelete(null);
                }}
              >
                Supprimer
              </Button>
            </div>
          </div>
        </div>
      )}

      {/* ── Add-ticket picker (#19/#41) — mounts at floatingWindowNested (60),
          above this sprint dialog. Multi-select so several tickets can be added
          in one pass; excludes tickets already in the target sprint. ── */}
      <TicketPicker
        open={pickerSprint !== null}
        projectId={projectId}
        title={
          pickerSprint
            ? `Ajouter des tickets au sprint « ${pickerSprint.name} »`
            : "Ajouter des tickets"
        }
        confirmLabel="Ajouter au sprint"
        selectionMode="multi"
        excludeRefs={items
          .filter((t) => t.sprintId === pickerSprint?.id)
          .map((t) => t.ref)}
        onSelect={(result) => {
          const picked = Array.isArray(result) ? result : [result];
          if (pickerSprint) {
            for (const p of picked) {
              void vm.assignSprint(p.ref, pickerSprint.id);
            }
          }
        }}
        onClose={() => setPickerSprint(null)}
      />
    </div>
  );
}
