"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { AlertCircle, GitBranch, Loader2, X } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ConfirmActionDialog } from "@/components/ui/confirm-action-dialog";
import { Input } from "@/components/ui/input";
import {
  type BoardForgejoRepositoriesResponse,
  type BoardForgejoRepositoryLink,
  type ForgejoRepository,
  getBoardForgejoRepositories,
  getForgejoRepositories,
  linkBoardForgejoRepository,
  unlinkBoardForgejoRepository,
} from "@/lib/api-forgejo";

interface BoardForgejoRepositoryLinksProps {
  boardId: string;
  canWrite?: boolean;
}

type LinkedRepository = BoardForgejoRepositoryLink & {
  repository: ForgejoRepository;
};

const normalizeBoardLinks = (
  result: BoardForgejoRepositoriesResponse,
): BoardForgejoRepositoryLink[] =>
  Array.isArray(result) ? result : (result.repositories ?? []);

const repositoryDisplayName = (repository: ForgejoRepository): string =>
  repository.display_name || `${repository.owner}/${repository.repo}`;

export function BoardForgejoRepositoryLinks({
  boardId,
  canWrite = false,
}: BoardForgejoRepositoryLinksProps) {
  const [linkedLinks, setLinkedLinks] = useState<BoardForgejoRepositoryLink[]>(
    [],
  );
  const [allRepos, setAllRepos] = useState<ForgejoRepository[]>([]);
  const [searchTerm, setSearchTerm] = useState("");
  const [isLoading, setIsLoading] = useState(true);
  const [linkError, setLinkError] = useState<string | null>(null);
  const [unlinkError, setUnlinkError] = useState<string | null>(null);
  const [isLinking, setIsLinking] = useState(false);
  const [isUnlinking, setIsUnlinking] = useState(false);
  const [unlinkTarget, setUnlinkTarget] = useState<LinkedRepository | null>(
    null,
  );

  const fetchLinkedRepos = useCallback(async () => {
    try {
      const result = await getBoardForgejoRepositories(boardId);
      setLinkedLinks(normalizeBoardLinks(result));
      setLinkError(null);
    } catch (err) {
      const message =
        err instanceof Error
          ? err.message
          : "Unable to load linked Git Project repositories.";
      setLinkError(message);
    }
  }, [boardId]);

  const fetchAllRepositories = useCallback(async () => {
    try {
      const repos = await getForgejoRepositories();
      setAllRepos(repos);
      setLinkError(null);
    } catch (err) {
      const message =
        err instanceof Error
          ? err.message
          : "Unable to load available Git Project repositories.";
      setLinkError(message);
    }
  }, []);

  useEffect(() => {
    let isMounted = true;

    const loadRepositories = async () => {
      setIsLoading(true);
      await Promise.all([fetchLinkedRepos(), fetchAllRepositories()]);
      if (isMounted) {
        setIsLoading(false);
      }
    };

    loadRepositories();

    return () => {
      isMounted = false;
    };
  }, [fetchAllRepositories, fetchLinkedRepos]);

  const linkedRepoIds = useMemo(
    () => new Set(linkedLinks.map((link) => link.repository_id)),
    [linkedLinks],
  );

  const repoById = useMemo(
    () => new Map(allRepos.map((repository) => [repository.id, repository])),
    [allRepos],
  );

  const linkedRepos = useMemo(
    () =>
      linkedLinks
        .map((link) => ({
          ...link,
          repository: link.repository ?? repoById.get(link.repository_id),
        }))
        .filter(
          (link): link is LinkedRepository => link.repository !== undefined,
        ),
    [linkedLinks, repoById],
  );

  const availableRepos = useMemo(() => {
    const query = searchTerm.toLowerCase().trim();

    return allRepos
      .filter((repository) => !linkedRepoIds.has(repository.id))
      .filter((repository) => {
        if (!query) {
          return true;
        }

        const haystack = [
          repositoryDisplayName(repository),
          repository.owner,
          repository.repo,
        ]
          .join(" ")
          .toLowerCase();

        return haystack.includes(query);
      });
  }, [allRepos, linkedRepoIds, searchTerm]);

  const handleLinkRepo = async (repositoryId: string) => {
    if (!canWrite) {
      return;
    }

    setIsLinking(true);
    setLinkError(null);

    try {
      await linkBoardForgejoRepository(boardId, repositoryId);
      await fetchLinkedRepos();
    } catch (err) {
      const message =
        err instanceof Error
          ? err.message
          : "Unable to link this repository to the board.";
      setLinkError(message);
    } finally {
      setIsLinking(false);
    }
  };

  const handleUnlinkRepo = async () => {
    if (!unlinkTarget || !canWrite) {
      return;
    }

    setIsUnlinking(true);
    setUnlinkError(null);

    try {
      await unlinkBoardForgejoRepository(boardId, unlinkTarget.repository_id);
      await fetchLinkedRepos();
      setUnlinkTarget(null);
    } catch (err) {
      const message =
        err instanceof Error
          ? err.message
          : "Unable to unlink this repository from the board.";
      setUnlinkError(message);
    } finally {
      setIsUnlinking(false);
    }
  };

  return (
    <>
      <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush sm:p-5">
        <div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
          <div className="min-w-0">
            <h3 className="text-sm font-semibold text-strong">
              Linked Git Project Repositories
            </h3>
            <p className="mt-1 text-sm text-muted">
              Choose which synced repositories appear on this Pipeline board.
            </p>
          </div>
          <Badge variant="outline" className="w-fit">
            {linkedRepos.length} linked
          </Badge>
        </div>

        {linkError && (
          <div className="mb-4 flex items-start gap-2 rounded-lg border border-[color:var(--danger)]/35 bg-[color:var(--danger-soft)] px-3 py-2 text-sm text-[color:var(--danger)]">
            <AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
            <span>{linkError}</span>
          </div>
        )}

        {isLoading ? (
          <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] px-4 py-6 text-center text-sm text-muted">
            <Loader2 className="mx-auto mb-2 h-4 w-4 animate-spin" />
            Loading Git Project repositories...
          </div>
        ) : (
          <div className="space-y-5">
            <div>
              <div className="mb-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted">
                On This Board
              </div>
              {linkedRepos.length === 0 ? (
                <div className="rounded-lg border border-dashed border-[color:var(--border)] bg-[color:var(--surface-muted)] px-4 py-5 text-center">
                  <div className="mx-auto mb-3 flex h-10 w-10 items-center justify-center rounded-full border border-[color:var(--border)] bg-[color:var(--surface)] text-muted">
                    <GitBranch className="h-5 w-5" />
                  </div>
                  <p className="text-sm font-medium text-strong">
                    No repositories linked yet
                  </p>
                  <p className="mt-1 text-sm text-muted">
                    {canWrite
                      ? "Link a Git Project repository below to bring its issues onto this board."
                      : "No Git Project repositories are linked to this board yet."}
                  </p>
                </div>
              ) : (
                <div className="grid gap-2 sm:grid-cols-2">
                  {linkedRepos.map((link) => {
                    const repository = link.repository;

                    return (
                      <div
                        key={link.id}
                        className="flex min-w-0 items-center justify-between gap-3 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-2"
                      >
                        <div className="min-w-0">
                          <p
                            className="truncate text-sm font-medium text-strong"
                            title={repositoryDisplayName(repository)}
                          >
                            {repositoryDisplayName(repository)}
                          </p>
                          <p
                            className="truncate text-xs text-muted"
                            title={`${repository.owner}/${repository.repo}`}
                          >
                            {repository.owner}/{repository.repo}
                          </p>
                        </div>
                        {canWrite ? (
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            className="h-8 w-8 shrink-0 p-0 text-[color:var(--danger)] hover:bg-[color:var(--danger-soft)]"
                            onClick={() => {
                              setUnlinkError(null);
                              setUnlinkTarget(link);
                            }}
                            aria-label={`Unlink ${repositoryDisplayName(repository)}`}
                          >
                            <X className="h-4 w-4" />
                          </Button>
                        ) : null}
                      </div>
                    );
                  })}
                </div>
              )}
            </div>

            {canWrite ? (
              <div>
                <div className="mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
                  <div className="min-w-0">
                    <div className="text-xs font-semibold uppercase tracking-[0.14em] text-muted">
                      Available Repositories
                    </div>
                    <p className="mt-1 text-sm text-muted">
                      Link repositories that are already configured in Git
                      Projects.
                    </p>
                  </div>
                  <Input
                    value={searchTerm}
                    onChange={(event) => setSearchTerm(event.target.value)}
                    placeholder="Search repositories..."
                    className="w-full sm:w-72"
                  />
                </div>

                {availableRepos.length === 0 ? (
                  <div className="rounded-lg border border-dashed border-[color:var(--border)] bg-[color:var(--surface-muted)] px-4 py-5 text-center">
                    <p className="text-sm font-medium text-strong">
                      {allRepos.length === 0
                        ? "No Git Project repositories configured"
                        : "No matching repositories"}
                    </p>
                    <p className="mt-1 text-sm text-muted">
                      {allRepos.length === 0
                        ? "Add repositories in Git Projects before linking them to boards."
                        : "Adjust the search or unlink a repository from this board."}
                    </p>
                  </div>
                ) : (
                  <div className="grid gap-2 lg:grid-cols-2">
                    {availableRepos.slice(0, 9).map((repository) => (
                      <div
                        key={repository.id}
                        className="flex min-w-0 flex-col gap-3 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-3 transition-colors hover:bg-[color:var(--accent-soft)] sm:flex-row sm:items-center sm:justify-between"
                      >
                        <div className="min-w-0">
                          <div className="flex min-w-0 flex-wrap items-center gap-2">
                            <p
                              className="truncate text-sm font-medium text-strong"
                              title={repositoryDisplayName(repository)}
                            >
                              {repositoryDisplayName(repository)}
                            </p>
                            <Badge
                              variant={
                                repository.active ? "success" : "outline"
                              }
                            >
                              {repository.active ? "Active" : "Paused"}
                            </Badge>
                          </div>
                          <p
                            className="mt-1 truncate text-xs text-muted"
                            title={`${repository.owner}/${repository.repo}`}
                          >
                            {repository.owner}/{repository.repo}
                          </p>
                        </div>
                        <Button
                          type="button"
                          variant="outline"
                          size="sm"
                          className="w-full shrink-0 sm:w-auto"
                          onClick={() => handleLinkRepo(repository.id)}
                          disabled={isLinking}
                        >
                          Link
                        </Button>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            ) : null}
          </div>
        )}
      </section>

      <ConfirmActionDialog
        open={unlinkTarget !== null}
        onOpenChange={(open) => {
          if (!open && !isUnlinking) {
            setUnlinkTarget(null);
            setUnlinkError(null);
          }
        }}
        title="Unlink Git Project repository"
        description={
          unlinkTarget
            ? `Remove "${repositoryDisplayName(unlinkTarget.repository)}" from this board? Issues from this repository will no longer appear on the board.`
            : "Remove this repository from the board?"
        }
        onConfirm={handleUnlinkRepo}
        isConfirming={isUnlinking}
        errorMessage={unlinkError}
        confirmLabel="Unlink Repository"
        confirmingLabel="Unlinking..."
        confirmClassName="bg-[color:var(--danger)] text-white hover:bg-[color:var(--danger)]/90"
        cancelLabel="Keep Linked"
      />
    </>
  );
}
