"use client";

export const dynamic = "force-dynamic";

import { useCallback, useEffect, useMemo, useState } from "react";
import type { ReactNode } from "react";
import Link from "next/link";
import {
  AlertCircle,
  CheckCircle2,
  CircleDot,
  Clock,
  Copy,
  ExternalLink,
  GitBranch,
  KeyRound,
  Link2,
  RefreshCw,
  Server,
  Webhook,
} from "lucide-react";

import { useAuth } from "@/auth/clerk";
import { ForgejoConnectionsTable } from "@/components/git/ForgejoConnectionsTable";
import { ForgejoRepositoriesTable } from "@/components/git/ForgejoRepositoriesTable";
import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout";
import { Button } from "@/components/ui/button";
import { ConfirmActionDialog } from "@/components/ui/confirm-action-dialog";
import { getApiBaseUrl } from "@/lib/api-base";
import {
  deleteForgejoConnection,
  deleteForgejoRepository,
  getForgejoConnections,
  getForgejoRepositories,
  syncRepository,
  validateConnection,
  validateRepository,
  type ForgejoConnection,
  type ForgejoRepository,
} from "@/lib/api-forgejo";

type Notice = {
  tone: "success" | "error";
  message: string;
};

type DeleteTarget =
  | { type: "connection"; item: ForgejoConnection }
  | { type: "repository"; item: ForgejoRepository };

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

const formatTimestamp = (value: string | null) => {
  if (!value) return "Never";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return date.toLocaleString();
};

function NoticeBanner({ notice }: { notice: Notice }) {
  return (
    <div
      className={`flex items-start gap-3 rounded-xl border p-3 text-sm ${
        notice.tone === "success"
          ? "border-[color:rgba(52,211,153,0.35)] bg-[color:rgba(52,211,153,0.08)] text-[color:var(--success)]"
          : "border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] text-[color:var(--danger)]"
      }`}
    >
      {notice.tone === "success" ? (
        <CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0" />
      ) : (
        <AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
      )}
      <span>{notice.message}</span>
    </div>
  );
}

function StatCard({
  icon,
  label,
  value,
  caption,
}: {
  icon: ReactNode;
  label: string;
  value: string;
  caption: string;
}) {
  return (
    <div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted">
            {label}
          </p>
          <p className="mt-3 break-words font-heading text-2xl font-semibold text-strong">
            {value}
          </p>
        </div>
        <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-2 text-[color:var(--accent)]">
          {icon}
        </div>
      </div>
      <p className="mt-3 text-sm text-muted">{caption}</p>
    </div>
  );
}

function CopyButton({ value, label }: { value: string; label?: string }) {
  const [copied, setCopied] = useState(false);

  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(value);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {
      // Clipboard API unavailable — silent fail
    }
  };

  return (
    <Button
      type="button"
      variant="ghost"
      size="sm"
      onClick={handleCopy}
      className="h-7 gap-1.5 px-2 text-xs"
      title={label ?? "Copy to clipboard"}
    >
      {copied ? (
        <CheckCircle2 className="h-3.5 w-3.5 text-[color:var(--success)]" />
      ) : (
        <Copy className="h-3.5 w-3.5" />
      )}
      {copied ? "Copied" : "Copy"}
    </Button>
  );
}

export default function GitProjectSettingsPage() {
  const auth = useAuth();
  const [connections, setConnections] = useState<ForgejoConnection[]>([]);
  const [repositories, setRepositories] = useState<ForgejoRepository[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [isSyncingAll, setIsSyncingAll] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState<Notice | null>(null);
  const [deleteTarget, setDeleteTarget] = useState<DeleteTarget | null>(null);
  const [deleteError, setDeleteError] = useState<string | null>(null);
  const [isDeleting, setIsDeleting] = useState(false);

  // Auto-dismiss notices after 8s
  useEffect(() => {
    if (!notice) return;
    const id = setTimeout(() => setNotice(null), 8000);
    return () => clearTimeout(id);
  }, [notice]);

  const loadSettings = useCallback(async () => {
    try {
      setIsLoading(true);
      const [connectionsData, repositoriesData] = await Promise.all([
        getForgejoConnections(),
        getForgejoRepositories(),
      ]);
      setConnections(connectionsData);
      setRepositories(repositoriesData);
      setError(null);
    } catch (err) {
      setError(
        err instanceof Error
          ? err.message
          : "Pipeline could not load Git Project settings.",
      );
    } finally {
      setIsLoading(false);
    }
  }, []);

  useEffect(() => {
    if (auth.isSignedIn) {
      loadSettings();
    }
  }, [auth.isSignedIn, loadSettings]);

  const activeConnections = useMemo(
    () => connections.filter((c) => c.active).length,
    [connections],
  );
  const activeRepositories = useMemo(
    () => repositories.filter((r) => r.active).length,
    [repositories],
  );
  const latestSync = useMemo(() => {
    const synced = repositories
      .map((r) => r.last_sync_at)
      .filter((v): v is string => Boolean(v))
      .map((v) => new Date(v))
      .filter((d) => !Number.isNaN(d.getTime()));
    if (synced.length === 0) return null;
    return synced.reduce((latest, d) =>
      d.getTime() > latest.getTime() ? d : latest,
    );
  }, [repositories]);
  const syncErrorCount = useMemo(
    () => repositories.filter((r) => r.last_sync_error).length,
    [repositories],
  );

  const webhookBaseUrl = useMemo(() => {
    try {
      return getApiBaseUrl();
    } catch {
      return "";
    }
  }, []);

  const handleValidateConnection = async (connection: ForgejoConnection) => {
    try {
      const result = await validateConnection(connection.id);
      setNotice(
        result.status.ok
          ? {
              tone: "success",
              message: `"${connection.name}" validated in ${Math.round(result.response_time_ms)}ms.`,
            }
          : {
              tone: "error",
              message: `Connection validation failed: ${result.status.error_message ?? "Unknown error"}`,
            },
      );
      return result;
    } catch (err) {
      setNotice({
        tone: "error",
        message:
          err instanceof Error ? err.message : "Failed to validate connection.",
      });
      throw err;
    }
  };

  const handleValidateRepository = async (repository: ForgejoRepository) => {
    try {
      const result = await validateRepository(repository.id);
      setNotice(
        result.status.ok
          ? {
              tone: "success",
              message: `${repositoryName(repository)} is reachable from Pipeline.`,
            }
          : {
              tone: "error",
              message: `Repository validation failed: ${result.status.error_message ?? "Unknown error"}`,
            },
      );
      return result;
    } catch (err) {
      setNotice({
        tone: "error",
        message:
          err instanceof Error
            ? err.message
            : "Failed to validate repository.",
      });
      throw err;
    }
  };

  const handleSyncRepository = async (repository: ForgejoRepository) => {
    try {
      const result = await syncRepository(repository.id);
      setNotice({
        tone: "success",
        message: `${repositoryName(repository)} synced: ${result.created} created, ${result.updated} updated, ${result.open} open, ${result.closed} closed.`,
      });
      const data = await getForgejoRepositories();
      setRepositories(data);
      return result;
    } catch (err) {
      setNotice({
        tone: "error",
        message:
          err instanceof Error ? err.message : "Failed to sync repository.",
      });
      throw err;
    }
  };

  const handleSyncAll = async () => {
    const active = repositories.filter((r) => r.active);
    if (active.length === 0) return;
    setIsSyncingAll(true);
    let succeeded = 0;
    let failed = 0;
    await Promise.allSettled(
      active.map(async (repo) => {
        try {
          await syncRepository(repo.id);
          succeeded++;
        } catch {
          failed++;
        }
      }),
    );
    const data = await getForgejoRepositories();
    setRepositories(data);
    setIsSyncingAll(false);
    setNotice(
      failed === 0
        ? {
            tone: "success",
            message: `All ${succeeded} repositories synced successfully.`,
          }
        : {
            tone: "error",
            message: `Sync completed: ${succeeded} succeeded, ${failed} failed.`,
          },
    );
  };

  const confirmDelete = async () => {
    if (!deleteTarget) return;
    setIsDeleting(true);
    setDeleteError(null);
    try {
      if (deleteTarget.type === "connection") {
        await deleteForgejoConnection(deleteTarget.item.id);
        setConnections((prev) =>
          prev.filter((c) => c.id !== deleteTarget.item.id),
        );
        setRepositories((prev) =>
          prev.filter((r) => r.connection_id !== deleteTarget.item.id),
        );
        setNotice({
          tone: "success",
          message: `Deleted "${deleteTarget.item.name}".`,
        });
      } else {
        await deleteForgejoRepository(deleteTarget.item.id);
        setRepositories((prev) =>
          prev.filter((r) => r.id !== deleteTarget.item.id),
        );
        setNotice({
          tone: "success",
          message: `Deleted "${repositoryName(deleteTarget.item)}".`,
        });
      }
      setDeleteTarget(null);
    } catch (err) {
      setDeleteError(
        err instanceof Error
          ? err.message
          : "Pipeline could not delete this item.",
      );
    } finally {
      setIsDeleting(false);
    }
  };

  return (
    <>
      <DashboardPageLayout
        signedOut={{
          message: "Sign in to manage Git Project settings.",
          forceRedirectUrl: "/settings/git-projects",
          signUpForceRedirectUrl: "/settings/git-projects",
        }}
        title="Git Project Settings"
        description="Manage Forgejo connections, tracked repositories, and issue sync."
        stickyHeader
        headerActions={
          <div className="flex flex-wrap gap-2">
            <Link href="/git-projects/issues">
              <Button variant="outline" size="sm">
                <CircleDot className="h-4 w-4" />
                Issues
              </Button>
            </Link>
            <Button
              type="button"
              variant="outline"
              size="sm"
              onClick={handleSyncAll}
              disabled={isSyncingAll || isLoading || activeRepositories === 0}
              title="Sync all active repositories"
            >
              <RefreshCw
                className={`h-4 w-4 ${isSyncingAll ? "animate-spin" : ""}`}
              />
              {isSyncingAll ? "Syncing…" : "Sync All"}
            </Button>
            <Button
              type="button"
              variant="outline"
              size="sm"
              onClick={loadSettings}
              disabled={isLoading}
            >
              <RefreshCw
                className={`h-4 w-4 ${isLoading ? "animate-spin" : ""}`}
              />
              Refresh
            </Button>
          </div>
        }
      >
        <div className="space-y-6">
          {notice ? <NoticeBanner notice={notice} /> : null}
          {error ? (
            <div className="flex items-start gap-3 rounded-xl border border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] p-3 text-sm text-[color:var(--danger)]">
              <AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
              <span>{error}</span>
            </div>
          ) : null}

          {/* Stats */}
          <section className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
            <StatCard
              icon={<Server className="h-4 w-4" />}
              label="Connections"
              value={`${activeConnections}/${connections.length}`}
              caption="Active Forgejo connection records."
            />
            <StatCard
              icon={<GitBranch className="h-4 w-4" />}
              label="Repositories"
              value={`${activeRepositories}/${repositories.length}`}
              caption="Active tracked repositories."
            />
            <StatCard
              icon={<RefreshCw className="h-4 w-4" />}
              label="Latest Sync"
              value={formatTimestamp(latestSync?.toISOString() ?? null)}
              caption="Most recent repository sync timestamp."
            />
            <StatCard
              icon={<AlertCircle className="h-4 w-4" />}
              label="Sync Errors"
              value={String(syncErrorCount)}
              caption="Repositories with a recorded sync error."
            />
          </section>

          {/* Connections */}
          <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
            <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
              <div className="min-w-0">
                <h2 className="text-base font-semibold text-strong">
                  Forgejo Connections
                </h2>
                <p className="mt-1 text-sm text-muted">
                  URL and token records used by tracked repositories.
                </p>
              </div>
              <Link href="/git-projects/connections/new">
                <Button size="sm">
                  <Link2 className="h-4 w-4" />
                  Add Connection
                </Button>
              </Link>
            </div>
            <div className="mt-4 overflow-hidden rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)]">
              <ForgejoConnectionsTable
                connections={connections}
                isLoading={isLoading}
                onDelete={(connection) =>
                  setDeleteTarget({ type: "connection", item: connection })
                }
                onValidate={handleValidateConnection}
              />
            </div>
          </section>

          {/* Repositories */}
          <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
            <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
              <div className="min-w-0">
                <h2 className="text-base font-semibold text-strong">
                  Tracked Repositories
                </h2>
                <p className="mt-1 text-sm text-muted">
                  Repositories whose issues are cached and shown in Pipeline.
                </p>
              </div>
              <div className="flex flex-wrap gap-2">
                <Link href="/git-projects/repositories/new">
                  <Button size="sm">
                    <GitBranch className="h-4 w-4" />
                    Add Repository
                  </Button>
                </Link>
                <Link href="/git-projects">
                  <Button variant="outline" size="sm">
                    <ExternalLink className="h-4 w-4" />
                    Git Projects
                  </Button>
                </Link>
              </div>
            </div>
            <div className="mt-4 overflow-hidden rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)]">
              <ForgejoRepositoriesTable
                repositories={repositories}
                isLoading={isLoading}
                onDelete={(repository) =>
                  setDeleteTarget({ type: "repository", item: repository })
                }
                onSync={handleSyncRepository}
                onValidate={handleValidateRepository}
              />
            </div>
          </section>

          {/* Webhook Setup */}
          <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
            <div className="flex items-start gap-3 mb-4">
              <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-2 text-[color:var(--accent)] shrink-0">
                <Webhook className="h-4 w-4" />
              </div>
              <div className="min-w-0">
                <h2 className="text-base font-semibold text-strong">
                  Webhook Setup
                </h2>
                <p className="mt-1 text-sm text-muted">
                  Configure webhooks in Forgejo to push issue updates to
                  Pipeline in real time, without waiting for the scheduled sync.
                </p>
              </div>
            </div>

            {repositories.length === 0 ? (
              <p className="text-sm text-muted">
                No repositories tracked yet. Add a repository to see webhook
                URLs.
              </p>
            ) : (
              <div className="space-y-3">
                <p className="text-xs text-muted">
                  In each Forgejo repository, go to{" "}
                  <strong className="text-strong">
                    Settings → Webhooks → Add Webhook
                  </strong>
                  . Set the target URL below, content type{" "}
                  <code className="rounded bg-[color:var(--surface-muted)] px-1 py-0.5 font-mono text-xs">
                    application/json
                  </code>
                  , and paste a secret. Store the same secret on the repository
                  record in Pipeline via{" "}
                  <Link
                    href="/git-projects/repositories"
                    className="text-[color:var(--accent)] underline underline-offset-2 hover:opacity-80"
                  >
                    Edit Repository
                  </Link>
                  .
                </p>
                <div className="overflow-x-auto rounded-xl border border-[color:var(--border)]">
                  <table className="min-w-full text-sm">
                    <thead>
                      <tr className="border-b border-[color:var(--border)] bg-[color:var(--surface-muted)]">
                        <th className="px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide text-muted">
                          Repository
                        </th>
                        <th className="px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide text-muted">
                          Webhook URL
                        </th>
                        <th className="px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide text-muted">
                          Secret
                        </th>
                      </tr>
                    </thead>
                    <tbody className="divide-y divide-[color:var(--border)]">
                      {repositories.map((repo) => {
                        const webhookUrl = webhookBaseUrl
                          ? `${webhookBaseUrl}/api/v1/forgejo/webhooks/${repo.id}`
                          : `…/api/v1/forgejo/webhooks/${repo.id}`;
                        return (
                          <tr
                            key={repo.id}
                            className="bg-[color:var(--surface)] hover:bg-[color:var(--surface-muted)]"
                          >
                            <td className="px-4 py-3">
                              <span className="font-medium text-strong">
                                {repositoryName(repo)}
                              </span>
                              <span className="ml-2 text-xs text-muted">
                                {repo.active ? "" : "(inactive)"}
                              </span>
                            </td>
                            <td className="px-4 py-3">
                              <div className="flex items-center gap-2">
                                <code className="max-w-[340px] truncate rounded bg-[color:var(--surface-muted)] px-2 py-0.5 font-mono text-xs text-strong">
                                  {webhookUrl}
                                </code>
                                <CopyButton
                                  value={webhookUrl}
                                  label="Copy webhook URL"
                                />
                              </div>
                            </td>
                            <td className="px-4 py-3">
                              {repo.has_webhook_secret ? (
                                <span className="inline-flex items-center gap-1.5 text-xs text-[color:var(--success)]">
                                  <KeyRound className="h-3.5 w-3.5" />
                                  Configured
                                </span>
                              ) : (
                                <span className="inline-flex items-center gap-1.5 text-xs text-muted">
                                  <KeyRound className="h-3.5 w-3.5" />
                                  Not set
                                </span>
                              )}
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
              </div>
            )}
          </section>

          {/* Scheduled Sync */}
          <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-5">
            <div className="flex items-start gap-3">
              <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-2 text-[color:var(--accent)] shrink-0">
                <Clock className="h-4 w-4" />
              </div>
              <div className="min-w-0">
                <h2 className="text-base font-semibold text-strong">
                  Scheduled Sync
                </h2>
                <p className="mt-1 text-sm text-muted">
                  Pipeline runs a background sync for all active repositories
                  every <strong className="text-strong">60 minutes</strong>.
                  This keeps issues current without manual syncing or webhooks.
                  The interval is configured via environment variable and cannot
                  be changed from the UI.
                </p>
                <div className="mt-3 flex flex-wrap gap-4 text-xs text-muted">
                  <span>
                    Env:{" "}
                    <code className="rounded bg-[color:var(--surface-muted)] px-1 py-0.5 font-mono text-strong">
                      FORGEJO_SYNC_ENABLED
                    </code>
                  </span>
                  <span>
                    Env:{" "}
                    <code className="rounded bg-[color:var(--surface-muted)] px-1 py-0.5 font-mono text-strong">
                      FORGEJO_SYNC_INTERVAL_SECONDS
                    </code>
                  </span>
                </div>
              </div>
            </div>
          </section>
        </div>
      </DashboardPageLayout>

      <ConfirmActionDialog
        open={Boolean(deleteTarget)}
        onOpenChange={(open) => {
          if (!open) setDeleteTarget(null);
        }}
        title={
          deleteTarget?.type === "connection"
            ? "Delete Git Project connection"
            : "Delete Git Project repository"
        }
        description={
          deleteTarget?.type === "connection"
            ? `Delete "${deleteTarget.item.name}" from Pipeline? Tracked repositories and cached issues that use this connection will be removed.`
            : deleteTarget?.type === "repository"
              ? `Delete "${repositoryName(deleteTarget.item)}" from Pipeline? Synced issue records for this repository will be removed.`
              : ""
        }
        onConfirm={confirmDelete}
        isConfirming={isDeleting}
        errorMessage={deleteError}
        confirmLabel="Delete"
        confirmingLabel="Deleting…"
        confirmClassName="bg-destructive text-destructive-foreground hover:bg-destructive/90"
        cancelLabel="Keep"
      />
    </>
  );
}
