"use client";

import Link from "next/link";
import { useEffect, useState } from "react";
import { ExternalLink } from "lucide-react";

import {
  useListAgentsApiV1AgentsGet,
  type listAgentsApiV1AgentsGetResponse,
} from "@/api/generated/agents/agents";
import {
  useListBoardsApiV1BoardsGet,
  type listBoardsApiV1BoardsGetResponse,
} from "@/api/generated/boards/boards";
import { ApiError } from "@/api/mutator";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import type { ForgejoIssue, AssignIssueAgentResponse } from "@/lib/api-forgejo";
import {
  assignIssueToAgent,
  getLinkedBoardsForRepository,
  linkBoardForgejoRepository,
} from "@/lib/api-forgejo";

type LinkedBoard = { id: string; name: string };

type AssignIssueAgentDialogProps = {
  issue: ForgejoIssue | null;
  repositoryName: string;
  open: boolean;
  onOpenChange: (open: boolean) => void;
  onSuccess: (result: AssignIssueAgentResponse) => void;
};

function buildDefaultAgentPrompt(issue: ForgejoIssue, repositoryName: string) {
  const issueBody = (issue.body ?? issue.body_preview ?? "").trim();
  const lines = [
    `Work on ${repositoryName}#${issue.forgejo_issue_number}: ${issue.title}`,
    "",
    "Use this Forgejo issue as the source of truth.",
    `Issue URL: ${issue.html_url}`,
  ];

  if (issueBody) {
    lines.push("", "Issue details:", issueBody);
  }

  lines.push(
    "",
    "Expected approach:",
    "- Read the relevant Pipeline code before editing.",
    "- Keep the work scoped to this issue.",
    "- Run targeted validation and report what passed.",
    "- Post progress, blockers, and handoff notes back to the Pipeline task.",
  );

  return lines.join("\n");
}

export function AssignIssueAgentDialog({
  issue,
  repositoryName,
  open,
  onOpenChange,
  onSuccess,
}: AssignIssueAgentDialogProps) {
  const [boardId, setBoardId] = useState("");
  const [agentId, setAgentId] = useState("");
  const [priority, setPriority] = useState("medium");
  const [instructions, setInstructions] = useState("");
  const [startImmediately, setStartImmediately] = useState(true);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [linkedBoards, setLinkedBoards] = useState<LinkedBoard[]>([]);
  const [boardsLoading, setBoardsLoading] = useState(false);
  const [linkedBoardsLoaded, setLinkedBoardsLoaded] = useState(false);
  const [linkBoardId, setLinkBoardId] = useState("");
  const [isLinkingRepository, setIsLinkingRepository] = useState(false);

  // Fetch only boards linked to this issue's repository — prevents picking a
  // board that the backend will reject with "not linked to any board".
  useEffect(() => {
    if (!open || !issue) return;
    let cancelled = false;
    setBoardsLoading(true);
    setLinkedBoardsLoaded(false);
    setLinkedBoards([]);
    setBoardId("");
    getLinkedBoardsForRepository(issue.repository_id)
      .then((boards) => {
        if (cancelled) return;
        setLinkedBoards(boards);
        if (boards.length === 1) setBoardId(boards[0].id);
      })
      .catch(() => {
        if (!cancelled) setLinkedBoards([]);
      })
      .finally(() => {
        if (!cancelled) {
          setBoardsLoading(false);
          setLinkedBoardsLoaded(true);
        }
      });
    return () => { cancelled = true; };
  }, [open, issue]);

  const agentsQuery = useListAgentsApiV1AgentsGet<
    listAgentsApiV1AgentsGetResponse,
    ApiError
  >(
    { board_id: boardId || undefined, limit: 200 },
    { query: { enabled: open && !!boardId, refetchOnMount: "always" } },
  );

  const boardsQuery = useListBoardsApiV1BoardsGet<
    listBoardsApiV1BoardsGetResponse,
    ApiError
  >(
    { limit: 200 },
    {
      query: {
        enabled:
          open &&
          linkedBoardsLoaded &&
          !boardsLoading &&
          linkedBoards.length === 0,
        refetchOnMount: "always",
      },
    },
  );

  const agents =
    agentsQuery.data?.status === 200
      ? (agentsQuery.data.data.items ?? [])
      : [];
  const allBoards =
    boardsQuery.data?.status === 200
      ? (boardsQuery.data.data.items ?? [])
      : [];

  useEffect(() => {
    if (!open) {
      setBoardId("");
      setAgentId("");
      setPriority("medium");
      setInstructions("");
      setStartImmediately(true);
      setError(null);
      setLinkedBoardsLoaded(false);
      setLinkBoardId("");
      setIsLinkingRepository(false);
    }
  }, [open]);

  useEffect(() => {
    if (!open || !issue) return;
    setInstructions(buildDefaultAgentPrompt(issue, repositoryName));
  }, [open, issue, repositoryName]);

  useEffect(() => {
    setAgentId("");
  }, [boardId]);

  if (!issue) return null;

  const noLinkedBoards =
    linkedBoardsLoaded && !boardsLoading && linkedBoards.length === 0;
  const prompt = instructions.trim();

  const handleSubmit = async () => {
    if (!boardId || !agentId || !prompt) return;
    setIsSubmitting(true);
    setError(null);
    try {
      const result = await assignIssueToAgent(issue.id, {
        board_id: boardId,
        assigned_agent_id: agentId,
        priority,
        start_immediately: startImmediately,
        status: startImmediately ? "in_progress" : "inbox",
        instructions: prompt,
      });
      onSuccess(result);
      onOpenChange(false);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to assign agent");
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleLinkRepositoryToBoard = async () => {
    if (!issue || !linkBoardId) return;
    setIsLinkingRepository(true);
    setError(null);

    try {
      await linkBoardForgejoRepository(linkBoardId, issue.repository_id);
      const linkedBoard = allBoards.find((board) => board.id === linkBoardId);
      const nextBoard = {
        id: linkBoardId,
        name: linkedBoard?.name ?? "Selected board",
      };
      setLinkedBoards([nextBoard]);
      setBoardId(nextBoard.id);
      setLinkBoardId("");
    } catch (err) {
      setError(
        err instanceof Error
          ? err.message
          : "Failed to link repository to board",
      );
    } finally {
      setIsLinkingRepository(false);
    }
  };

  const toggleSwitch = (
    value: boolean,
    setter: (v: boolean) => void,
    disabled: boolean,
  ) => (
    <button
      type="button"
      role="switch"
      aria-checked={value}
      onClick={() => !disabled && setter(!value)}
      disabled={disabled}
      className={`mt-0.5 inline-flex h-6 w-11 shrink-0 items-center rounded-full border transition ${
        value
          ? "border-emerald-600 bg-emerald-600"
          : "border-[color:var(--border)] bg-[color:var(--surface-muted)]"
      } ${disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer"}`}
    >
      <span
        className={`inline-block h-5 w-5 rounded-full bg-[color:var(--surface)] shadow-sm transition ${
          value ? "translate-x-5" : "translate-x-0.5"
        }`}
      />
    </button>
  );

  return (
    <Dialog
      open={open}
      onOpenChange={(next) => {
        if (!isSubmitting) onOpenChange(next);
      }}
    >
      <DialogContent className="max-w-lg">
        <DialogHeader>
          <DialogTitle>Assign to agent</DialogTitle>
          <DialogDescription>
            Create a Pipeline task for{" "}
            <span className="font-mono font-semibold text-strong">
              {repositoryName}#{issue.forgejo_issue_number}
            </span>{" "}
            and assign an agent to work on it.
          </DialogDescription>
        </DialogHeader>

        <div className="space-y-4">
          <div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-2">
            <p className="truncate text-sm font-medium text-strong">
              {issue.title}
            </p>
          </div>

          {noLinkedBoards ? (
            <div className="rounded-xl border border-[color:rgba(234,179,8,0.4)] bg-[color:rgba(234,179,8,0.08)] p-4 text-sm">
              <p className="font-semibold text-[color:var(--warning,#ca8a04)]">
                Repository not linked to any board
              </p>
              <p className="mt-1 text-muted">
                Before you can assign an agent, link{" "}
                <span className="font-medium text-strong">{repositoryName}</span>{" "}
                to the board that should own this task. Open the target board and
                use its Git Project repositories panel, or manage tracked
                repositories first.
              </p>
              <div className="mt-4 space-y-2">
                <label className="text-sm font-medium text-strong">
                  Link to board <span className="text-[color:var(--danger)]">*</span>
                </label>
                <select
                  value={linkBoardId}
                  onChange={(e) => setLinkBoardId(e.target.value)}
                  disabled={boardsQuery.isLoading || isLinkingRepository}
                  className="w-full rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-sm text-strong focus:outline-none focus:ring-2 focus:ring-[color:var(--accent)]"
                >
                  <option value="">
                    {boardsQuery.isLoading ? "Loading boards..." : "Select a board..."}
                  </option>
                  {allBoards.map((board) => (
                    <option key={board.id} value={board.id}>
                      {board.name}
                    </option>
                  ))}
                </select>
                {boardsQuery.isError ? (
                  <p className="text-xs text-[color:var(--danger)]">
                    Unable to load boards. You can still manage repositories
                    from Git Projects.
                  </p>
                ) : null}
              </div>
              <div className="mt-3 flex flex-wrap gap-2">
                <Button
                  size="sm"
                  onClick={handleLinkRepositoryToBoard}
                  disabled={
                    !linkBoardId || boardsQuery.isLoading || isLinkingRepository
                  }
                >
                  {isLinkingRepository ? "Linking..." : "Link and continue"}
                </Button>
                <Link
                  href="/git-projects/repositories"
                  className="inline-flex h-9 items-center gap-2 rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] px-3 text-xs font-semibold text-strong transition hover:border-[color:var(--accent)] hover:text-[color:var(--accent)]"
                >
                  Manage Git repositories
                  <ExternalLink className="h-3.5 w-3.5" />
                </Link>
              </div>
            </div>
          ) : (
            <>
              <div className="space-y-1.5">
                <label className="text-sm font-medium text-strong">
                  Board <span className="text-[color:var(--danger)]">*</span>
                </label>
                <select
                  value={boardId}
                  onChange={(e) => setBoardId(e.target.value)}
                  disabled={isSubmitting || boardsLoading}
                  className="w-full rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-sm text-strong focus:outline-none focus:ring-2 focus:ring-[color:var(--accent)]"
                >
                  {boardsLoading ? (
                    <option>Loading boards…</option>
                  ) : (
                    <>
                      {linkedBoards.length > 1 && (
                        <option value="">Select a board…</option>
                      )}
                      {linkedBoards.map((b) => (
                        <option key={b.id} value={b.id}>
                          {b.name}
                        </option>
                      ))}
                    </>
                  )}
                </select>
              </div>

              <div className="space-y-1.5">
                <label className="text-sm font-medium text-strong">
                  Agent <span className="text-[color:var(--danger)]">*</span>
                </label>
                <select
                  value={agentId}
                  onChange={(e) => setAgentId(e.target.value)}
                  disabled={isSubmitting || !boardId || agentsQuery.isLoading}
                  className="w-full rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-sm text-strong focus:outline-none focus:ring-2 focus:ring-[color:var(--accent)]"
                >
                  <option value="">Select an agent…</option>
                  {agentsQuery.isLoading ? (
                    <option disabled>Loading agents…</option>
                  ) : (
                    agents.map((a) => (
                      <option key={a.id} value={a.id}>
                        {a.name}
                        {a.is_board_lead ? " (lead)" : ""}
                      </option>
                    ))
                  )}
                </select>
                {boardId && !agentsQuery.isLoading && agents.length === 0 ? (
                  <p className="text-xs text-muted">
                    No agents are available on this board yet.
                  </p>
                ) : null}
              </div>

              <div className="space-y-1.5">
                <label className="text-sm font-medium text-strong">Priority</label>
                <select
                  value={priority}
                  onChange={(e) => setPriority(e.target.value)}
                  disabled={isSubmitting}
                  className="w-full rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-sm text-strong focus:outline-none focus:ring-2 focus:ring-[color:var(--accent)]"
                >
                  <option value="low">Low</option>
                  <option value="medium">Medium</option>
                  <option value="high">High</option>
                  <option value="critical">Critical</option>
                </select>
              </div>

              <div className="space-y-1.5">
                <label className="text-sm font-medium text-strong">
                  Agent prompt <span className="text-[color:var(--danger)]">*</span>
                </label>
                <Textarea
                  value={instructions}
                  onChange={(e) => setInstructions(e.target.value)}
                  disabled={isSubmitting}
                  rows={9}
                  placeholder="Prompt the agent with the goal, context, and expected validation…"
                  className="resize-y text-sm"
                />
                <p className="text-xs text-muted">
                  This prompt is copied into the Pipeline task and sent with the
                  assignment notification.
                </p>
              </div>

              <div className="flex items-center gap-3">
                {toggleSwitch(startImmediately, setStartImmediately, isSubmitting)}
                <span className="space-y-0.5">
                  <span className="block text-sm font-medium text-strong">
                    Start immediately
                  </span>
                  <span className="block text-xs text-muted">
                    Set task to <span className="font-mono">in_progress</span>{" "}
                    when assigned.
                  </span>
                </span>
              </div>
            </>
          )}
        </div>

        {error ? (
          <div className="rounded-lg border border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] p-3 text-xs text-[color:var(--danger)]">
            {error}
          </div>
        ) : null}

        <DialogFooter>
          <Button
            variant="outline"
            onClick={() => onOpenChange(false)}
            disabled={isSubmitting}
          >
            Cancel
          </Button>
          {!noLinkedBoards && (
            <Button
              onClick={handleSubmit}
              disabled={isSubmitting || !boardId || !agentId || !prompt}
            >
              {isSubmitting ? "Assigning…" : "Assign Agent"}
            </Button>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
