"use client";

import { useEffect, useState, type ComponentType } from "react";
import Link from "next/link";
import {
  AlertCircle,
  AlertTriangle,
  ArrowUpRight,
  CheckCircle2,
  Clock3,
  PlusCircle,
  RefreshCw,
  ShieldAlert,
  ShieldCheck,
} from "lucide-react";

import type { ForgejoIssueMetrics, ForgejoRepository } from "@/lib/api-forgejo";
import { cn } from "@/lib/utils";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

type ForgejoIssueMetricCardsProps = {
  metrics: ForgejoIssueMetrics | null;
  repositories: ForgejoRepository[];
  metricRepositories?: ForgejoRepository[];
  selectedRepositoryId: string;
  onSelectedRepositoryChange: (repositoryId: string) => void;
  onRefreshLastSync: () => void;
  onCreateIssue: () => void;
  isRefreshingLastSync?: boolean;
  isLoading?: boolean;
  error?: string | null;
};

// Extended tone set — covers every semantic state
type MetricTone = "amber" | "success" | "danger" | "cyan" | "slate" | "muted";

type MetricCard = {
  title: string;
  value: string;
  caption: string;
  href: string;
  tone: MetricTone;
  icon: ComponentType<{ className?: string }>;
  action?: {
    label: string;
    isLoading?: boolean;
    onClick: () => void;
  };
};

const numberFormatter = new Intl.NumberFormat("en-US");
const STALE_SYNC_THRESHOLD_MS = 24 * 60 * 60 * 1000;
const ALL_REPOSITORIES_VALUE = "all";

const formatCount = (value: number | null | undefined): string =>
  numberFormatter.format(Math.max(0, Math.round(Number(value ?? 0))));

const parseDate = (value: string | null | undefined): Date | null => {
  if (!value) return null;
  const hasTimezone = /(?:Z|[+-]\d{2}:?\d{2})$/i.test(value);
  const date = new Date(hasTimezone ? value : `${value}Z`);
  return Number.isNaN(date.getTime()) ? null : date;
};

const formatRelativeTimestampLive = (date: Date, nowMs: number): string => {
  const diff = Math.max(0, nowMs - date.getTime());
  const seconds = Math.round(diff / 1000);
  if (seconds < 60) return `${seconds}s ago`;
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
};

const newestDate = (dates: Date[]): Date | null => {
  if (dates.length === 0) return null;
  return dates.reduce((latest, date) =>
    date.getTime() > latest.getTime() ? date : latest,
  );
};

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

// ── Tone → icon badge (background + text color) ────────────────────────────
const toneIconClasses: Record<MetricTone, string> = {
  amber:   "border-[color:rgba(245,158,11,0.35)] bg-[color:rgba(245,158,11,0.14)] text-[color:#F59E0B]",
  success: "border-[color:rgba(16,185,129,0.35)] bg-[color:rgba(16,185,129,0.14)] text-[color:#10B981]",
  danger:  "border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.14)] text-[color:var(--danger)]",
  cyan:    "border-[color:rgba(6,182,212,0.35)]   bg-[color:rgba(6,182,212,0.14)]   text-[color:#06B6D4]",
  slate:   "border-[color:rgba(100,116,139,0.35)] bg-[color:rgba(100,116,139,0.12)] text-[color:#64748B]",
  muted:   "border-[color:var(--border)]           bg-[color:var(--surface-muted)]   text-muted",
};

// ── Tone → card (full background + border + hover glow) ───────────────────
const toneCardClasses: Record<MetricTone, string> = {
  amber:   "border-[color:rgba(245,158,11,0.28)]  bg-[color:rgba(245,158,11,0.06)]  hover:border-[color:rgba(245,158,11,0.55)]  hover:shadow-[0_4px_24px_rgba(245,158,11,0.18)]",
  success: "border-[color:rgba(16,185,129,0.28)]  bg-[color:rgba(16,185,129,0.06)]  hover:border-[color:rgba(16,185,129,0.55)]  hover:shadow-[0_4px_24px_rgba(16,185,129,0.18)]",
  danger:  "border-[color:rgba(248,113,113,0.28)] bg-[color:rgba(248,113,113,0.06)] hover:border-[color:rgba(248,113,113,0.55)] hover:shadow-[0_4px_24px_rgba(248,113,113,0.18)]",
  cyan:    "border-[color:rgba(6,182,212,0.28)]   bg-[color:rgba(6,182,212,0.06)]   hover:border-[color:rgba(6,182,212,0.55)]   hover:shadow-[0_4px_24px_rgba(6,182,212,0.18)]",
  slate:   "border-[color:rgba(100,116,139,0.22)] bg-[color:rgba(100,116,139,0.05)] hover:border-[color:rgba(100,116,139,0.40)] hover:shadow-[0_4px_16px_rgba(100,116,139,0.12)]",
  muted:   "border-[color:var(--border)]           bg-[color:var(--surface-muted)]   hover:border-[color:var(--border-strong)]   hover:shadow-sm",
};

// ── Tone → value text color ────────────────────────────────────────────────
const toneValueClasses: Record<MetricTone, string> = {
  amber:   "text-[color:#F59E0B]",
  success: "text-[color:#10B981]",
  danger:  "text-[color:var(--danger)]",
  cyan:    "text-[color:#06B6D4]",
  slate:   "text-[color:#64748B]",
  muted:   "text-strong",
};

// ── Card builder: Last Sync Health ─────────────────────────────────────────
function buildSyncHealthCard(
  metrics: ForgejoIssueMetrics | null,
  repositories: ForgejoRepository[],
  nowMs: number | null,
  onRefreshLastSync: () => void,
  isRefreshingLastSync: boolean,
): MetricCard {
  const repositoryCount = repositories.length;
  const syncErrorCount = Object.values(metrics?.sync_error_counts ?? {}).filter(
    (count) => Number(count) > 0,
  ).length;
  const metricSyncDates = Object.values(metrics?.last_sync_timestamps ?? {}).flatMap(
    (v) => { const d = parseDate(v); return d ? [d] : []; },
  );
  const repositorySyncDates = repositories.flatMap(
    (r) => { const d = parseDate(r.last_sync_at); return d ? [d] : []; },
  );
  const latestSync = newestDate(
    metricSyncDates.length > 0 ? metricSyncDates : repositorySyncDates,
  );
  const latestSyncAge = latestSync && nowMs != null
    ? Math.max(0, nowMs - latestSync.getTime())
    : 0;

  if (repositoryCount === 0) {
    return {
      title: "Last Sync",
      value: "No repos",
      caption: "Add repositories to track sync status.",
      href: "/git-projects/repositories",
      tone: "muted",
      icon: RefreshCw,
    };
  }
  if (syncErrorCount > 0) {
    return {
      title: "Last Sync",
      value: `${formatCount(syncErrorCount)} repo${syncErrorCount === 1 ? "" : "s"}`,
      caption: "Repository sync needs attention.",
      href: "/git-projects/repositories",
      tone: "danger",
      icon: ShieldAlert,
      action: {
        label: "Refresh Last Sync",
        isLoading: isRefreshingLastSync,
        onClick: onRefreshLastSync,
      },
    };
  }
  if (!latestSync) {
    return {
      title: "Last Sync",
      value: "Waiting",
      caption: "Repositories have not synced yet.",
      href: "/git-projects/repositories",
      tone: "slate",
      icon: Clock3,
      action: {
        label: "Refresh Last Sync",
        isLoading: isRefreshingLastSync,
        onClick: onRefreshLastSync,
      },
    };
  }
  if (latestSyncAge > STALE_SYNC_THRESHOLD_MS) {
    return {
      title: "Last Sync",
      value: "Stale",
      caption: `Updated ${formatRelativeTimestampLive(latestSync, nowMs ?? latestSync.getTime())}.`,
      href: "/git-projects/repositories",
      tone: "amber",
      icon: Clock3,
      action: {
        label: "Refresh Last Sync",
        isLoading: isRefreshingLastSync,
        onClick: onRefreshLastSync,
      },
    };
  }
  return {
    title: "Last Sync",
    value: "Healthy",
    caption: `Updated ${formatRelativeTimestampLive(latestSync, nowMs ?? latestSync.getTime())}.`,
    href: "/git-projects/repositories",
    tone: "cyan",
    icon: ShieldCheck,
    action: {
      label: "Refresh Last Sync",
      isLoading: isRefreshingLastSync,
      onClick: onRefreshLastSync,
    },
  };
}

// ── Skeleton ───────────────────────────────────────────────────────────────
function MetricSkeleton() {
  return (
    <div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-4">
      <div className="h-4 w-28 animate-pulse rounded bg-[color:var(--surface-strong)]" />
      <div className="mt-4 h-8 w-16 animate-pulse rounded bg-[color:var(--surface-strong)]" />
      <div className="mt-3 h-3 w-36 animate-pulse rounded bg-[color:var(--surface-strong)]" />
    </div>
  );
}

// ── Card renderer ──────────────────────────────────────────────────────────
function MetricCardLink({ card }: { card: MetricCard }) {
  const Icon = card.icon;
  const valueParts = card.value.split(/(\d[\d,]*)/g);

  return (
    <div
      className={cn(
        "group relative flex min-w-0 flex-col justify-between rounded-xl border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-sm",
        toneCardClasses[card.tone],
      )}
    >
      <Link
        href={card.href}
        className="absolute inset-0 rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)]"
        aria-label={`Open ${card.title}`}
      />
      <div className="relative z-10 flex items-start justify-between gap-3 pointer-events-none">
        <div className="min-w-0">
          <p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted">
            {card.title}
          </p>
          <p
            className={cn(
              "mt-3 break-words font-heading text-3xl font-semibold",
              toneValueClasses[card.tone],
            )}
          >
            {valueParts.map((part, index) =>
              /^\d[\d,]*$/.test(part) ? (
                <span key={`${part}-${index}`} className="font-numeric">
                  {part}
                </span>
              ) : (
                part
              ),
            )}
          </p>
        </div>
        <div className="flex shrink-0 items-center gap-1.5 pointer-events-auto">
          <div
            className={cn(
              "rounded-lg border p-2 transition-transform duration-200 group-hover:scale-110",
              toneIconClasses[card.tone],
            )}
          >
            <Icon className="h-4 w-4" />
          </div>
        </div>
      </div>
      <div className="relative z-10 mt-4 flex items-center justify-between gap-3 pointer-events-none">
        <p className="min-w-0 text-sm text-muted">{card.caption}</p>
        {card.action ? (
          <button
            type="button"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              card.action?.onClick();
            }}
            disabled={card.action.isLoading}
            className="pointer-events-auto -m-1 rounded-md p-1 text-muted transition hover:text-[color:var(--accent)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)] disabled:cursor-not-allowed disabled:opacity-70"
            title={card.action.label}
            aria-label={card.action.label}
          >
            <RefreshCw
              className={cn("h-4 w-4 shrink-0", card.action.isLoading && "animate-spin")}
            />
          </button>
        ) : (
          <ArrowUpRight className="h-4 w-4 shrink-0 text-muted transition group-hover:text-[color:var(--accent)]" />
        )}
      </div>
    </div>
  );
}

// ── Main export ────────────────────────────────────────────────────────────
export function ForgejoIssueMetricCards({
  metrics,
  repositories,
  metricRepositories,
  selectedRepositoryId,
  onSelectedRepositoryChange,
  onRefreshLastSync,
  onCreateIssue,
  isRefreshingLastSync = false,
  isLoading = false,
  error,
}: ForgejoIssueMetricCardsProps) {
  const [nowMs, setNowMs] = useState<number | null>(null);

  useEffect(() => {
    const initialTick = window.setTimeout(() => setNowMs(Date.now()), 0);
    const id = window.setInterval(() => setNowMs(Date.now()), 1000);
    return () => {
      window.clearTimeout(initialTick);
      window.clearInterval(id);
    };
  }, []);

  const activeRepositories = metricRepositories ?? repositories;
  const openIssues    = metrics?.open_issues        ?? 0;
  const recentlyClosed = metrics?.closed_last_7_days ?? 0;
  const staleOpen     = metrics?.stale_open_issues   ?? 0;
  const repositoriesSynced = metrics?.repositories_synced ?? activeRepositories.length;

  // Stale: 0 → slate, 1–5 → amber, >5 → danger
  const staleTone: MetricTone =
    staleOpen === 0 ? "slate" :
    staleOpen <= 5  ? "amber" :
    "danger";
  const issueHref = (params: string): string => {
    const repositoryParam =
      selectedRepositoryId === ALL_REPOSITORIES_VALUE
        ? ""
        : `&repository_id=${encodeURIComponent(selectedRepositoryId)}`;
    return `/git-projects/issues?${params}${repositoryParam}`;
  };

  const cards: MetricCard[] = [
    {
      title: "Open Issues",
      value: formatCount(openIssues),
      caption:
        openIssues === 0
          ? "No open Git Project issues."
          : "Review open issues across Git Projects.",
      href: issueHref("state=open"),
      tone: openIssues > 0 ? "amber" : "muted",
      icon: openIssues > 0 ? AlertCircle : CheckCircle2,
    },
    {
      title: "Recently Closed",
      value: formatCount(recentlyClosed),
      caption: "Closed in the last 7 days.",
      href: issueHref("state=closed&recent=7d"),
      tone: recentlyClosed > 0 ? "success" : "muted",
      icon: CheckCircle2,
    },
    {
      title: "Stale Open Issues",
      value: formatCount(staleOpen),
      caption:
        staleOpen === 0
          ? "Nothing abandoned right now."
          : "Open issues without recent movement.",
      href: issueHref("state=open&stale=1"),
      tone: staleTone,
      icon: staleOpen === 0 ? CheckCircle2 : Clock3,
    },
    buildSyncHealthCard(
      metrics,
      activeRepositories,
      nowMs,
      onRefreshLastSync,
      isRefreshingLastSync,
    ),
  ];

  return (
    <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush md:p-6">
      <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-lg font-semibold text-strong">
            Git Project Issue Tracking
          </h3>
          <p className="mt-1 text-sm text-muted">
            High-level Forgejo issue health across repositories synced into Pipeline.
          </p>
        </div>
        <div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
          <Select
            value={selectedRepositoryId}
            onValueChange={onSelectedRepositoryChange}
            disabled={repositories.length === 0}
          >
            <SelectTrigger className="h-9 w-full min-w-[190px] rounded-lg border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 text-sm shadow-none focus:ring-offset-0 sm:w-[230px]">
              <SelectValue placeholder="All projects" />
            </SelectTrigger>
            <SelectContent align="end">
              <SelectItem value={ALL_REPOSITORIES_VALUE}>All projects</SelectItem>
              {repositories.map((repository) => (
                <SelectItem key={repository.id} value={repository.id}>
                  {repositoryLabel(repository)}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <button
            type="button"
            onClick={onCreateIssue}
            disabled={repositories.length === 0}
            className="inline-flex w-fit items-center gap-1 text-sm font-medium text-[color:var(--accent)] transition hover:text-[color:var(--accent-strong)] disabled:cursor-not-allowed disabled:opacity-50"
          >
            Create issue
            <PlusCircle className="h-4 w-4" />
          </button>
        </div>
      </div>

      {/* Warning banner — left accent border, brighter amber */}
      {error ? (
        <div className="mb-4 flex items-start gap-3 rounded-lg border border-[color:rgba(245,158,11,0.45)] bg-[color:rgba(245,158,11,0.10)] p-3 text-sm text-[color:#F59E0B] [border-left:3px_solid_#F59E0B]">
          <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-[color:#F59E0B]" />
          <span className="font-medium">{error}</span>
        </div>
      ) : null}

      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-4">
        {isLoading
          ? Array.from({ length: 4 }).map((_, i) => <MetricSkeleton key={i} />)
          : cards.map((card) => <MetricCardLink key={card.title} card={card} />)}
      </div>

      {!isLoading && !error && repositories.length === 0 ? (
        <div className="mt-4 rounded-lg border border-dashed border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-sm text-muted">
          No Git Project repositories are configured yet. Metrics will populate after repositories are added and synced.
        </div>
      ) : !isLoading && !error && repositoriesSynced === 0 ? (
        <div className="mt-4 rounded-lg border border-dashed border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-sm text-muted">
          Git Project repositories are configured, but Pipeline has not synced issue metrics yet.
        </div>
      ) : null}
    </section>
  );
}
