"use client";

export const dynamic = "force-dynamic";

import { useState, useEffect, useMemo } from "react";
import { useSearchParams } from "next/navigation";
import { AlertCircle } from "lucide-react";

import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout";
import { Button } from "@/components/ui/button";
import { ApiError } from "@/api/mutator";
import {
  type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
  useGetMyMembershipApiV1OrganizationsMeMemberGet,
} from "@/api/generated/organizations/organizations";
import {
  getForgejoIssues,
  getForgejoRepositories,
  type ForgejoIssue,
  type ForgejoRepository,
} from "@/lib/api-forgejo";
import { ForgejoIssueFilters } from "@/components/git/ForgejoIssueFilters";
import { ForgejoIssuesTable } from "@/components/git/ForgejoIssuesTable";

const STALE_ISSUE_DAYS = 14;
const STALE_ISSUE_MS = STALE_ISSUE_DAYS * 24 * 60 * 60 * 1000;
const RECENT_CLOSED_DAYS = 7;
const RECENT_CLOSED_MS = RECENT_CLOSED_DAYS * 24 * 60 * 60 * 1000;
const ISSUE_STATES = new Set(["all", "open", "closed"]);

const normalizeStateFilter = (value: string | null): string =>
  value && ISSUE_STATES.has(value) ? value : "open";

const parsePositiveInteger = (value: string | null): number => {
  const parsed = Number.parseInt(value ?? "", 10);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
};

const isStaleOpenIssue = (issue: ForgejoIssue, cutoffMs: number): boolean => {
  if (issue.state !== "open") return false;
  const updatedAt = new Date(issue.forgejo_updated_at || issue.updated_at);
  if (Number.isNaN(updatedAt.getTime())) return false;
  return updatedAt.getTime() < cutoffMs;
};

const isRecentlyClosedIssue = (
  issue: ForgejoIssue,
  cutoffMs: number,
): boolean => {
  if (issue.state !== "closed") return false;
  const closedAt = new Date(
    issue.forgejo_closed_at || issue.forgejo_updated_at || issue.updated_at,
  );
  if (Number.isNaN(closedAt.getTime())) return false;
  return closedAt.getTime() >= cutoffMs;
};

export default function GitIssuesPage() {
  const searchParams = useSearchParams();
  const initialStaleOnly = searchParams.get("stale") === "1";
  const initialRecentClosedOnly =
    !initialStaleOnly && searchParams.get("recent") === "7d";
  const [issues, setIssues] = useState<ForgejoIssue[]>([]);
  const [repos, setRepos] = useState<ForgejoRepository[]>([]);
  const [total, setTotal] = useState(0);
  const [isLoadingIssues, setIsLoadingIssues] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [stateFilter, setStateFilter] = useState<string>(() =>
    initialStaleOnly
      ? "open"
      : initialRecentClosedOnly
        ? "closed"
        : normalizeStateFilter(searchParams.get("state")),
  );
  const [repoFilter, setRepoFilter] = useState<string>(
    () => searchParams.get("repository_id") || "all",
  );
  const [search, setSearch] = useState(() => searchParams.get("search") ?? "");
  const [staleOnly, setStaleOnly] = useState(initialStaleOnly);
  const [recentClosedOnly, setRecentClosedOnly] = useState(
    initialRecentClosedOnly,
  );
  const [page, setPage] = useState(() =>
    parsePositiveInteger(searchParams.get("page")),
  );
  const limit = 30;
  const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet<
    getMyMembershipApiV1OrganizationsMeMemberGetResponse,
    ApiError
  >({
    query: {
      enabled: true,
      refetchOnMount: "always",
    },
  });

  useEffect(() => {
    (async () => {
      try {
        const repos = await getForgejoRepositories();
        setRepos(repos);
      } catch (err) {
        console.error("Failed to fetch repositories:", err);
      }
    })();
  }, []);

  useEffect(() => {
    const controller = new AbortController();
    (async () => {
      try {
        setIsLoadingIssues(true);
        const result = await getForgejoIssues({
          state: staleOnly
            ? "open"
            : recentClosedOnly
              ? "closed"
              : stateFilter !== "all"
                ? stateFilter
                : undefined,
          repository_id: repoFilter !== "all" ? repoFilter : undefined,
          search: search || undefined,
          page: staleOnly || recentClosedOnly ? 1 : page,
          limit: staleOnly || recentClosedOnly ? 100 : limit,
        });
        setIssues(result.items);
        setTotal(result.total);
        setError(null);
      } catch (err) {
        if (err instanceof Error && err.name === "AbortError") return;
        setError(
          err instanceof Error
            ? err.message
            : "Pipeline could not load Git Project issues.",
        );
      } finally {
        setIsLoadingIssues(false);
      }
    })();
    return () => controller.abort();
  }, [stateFilter, repoFilter, search, staleOnly, recentClosedOnly, page]);

  const handleRefresh = async () => {
    try {
      setIsLoadingIssues(true);
      const result = await getForgejoIssues({
        state: staleOnly
          ? "open"
          : recentClosedOnly
            ? "closed"
            : stateFilter !== "all"
              ? stateFilter
              : undefined,
        repository_id: repoFilter !== "all" ? repoFilter : undefined,
        search: search || undefined,
        page: staleOnly || recentClosedOnly ? 1 : page,
        limit: staleOnly || recentClosedOnly ? 100 : limit,
      });
      setIssues(result.items);
      setTotal(result.total);
      setError(null);
    } catch (err) {
      setError(
        err instanceof Error
          ? err.message
          : "Pipeline could not refresh Git Project issues.",
      );
    } finally {
      setIsLoadingIssues(false);
    }
  };

  const staleCutoffMs = useMemo(() => Date.now() - STALE_ISSUE_MS, []);
  const recentClosedCutoffMs = useMemo(() => Date.now() - RECENT_CLOSED_MS, []);
  const canCloseIssues = useMemo(() => {
    if (membershipQuery.data?.status !== 200) {
      return false;
    }
    const member = membershipQuery.data.data;
    if (["owner", "admin"].includes(member.role)) {
      return true;
    }
    if (member.all_boards_write) {
      return true;
    }
    return (member.board_access ?? []).some((entry) => entry.can_write);
  }, [membershipQuery.data]);
  const visibleIssues = useMemo(() => {
    if (staleOnly) {
      return issues.filter((issue) => isStaleOpenIssue(issue, staleCutoffMs));
    }
    if (recentClosedOnly) {
      return issues.filter((issue) =>
        isRecentlyClosedIssue(issue, recentClosedCutoffMs),
      );
    }
    // Filter out pull requests — show only issues
    return issues.filter((issue) => !issue.is_pull_request);
  }, [
    issues,
    recentClosedCutoffMs,
    recentClosedOnly,
    staleCutoffMs,
    staleOnly,
  ]);
  const isClientFiltered = staleOnly || recentClosedOnly;
  const visibleTotal = isClientFiltered ? visibleIssues.length : total;
  const totalPages = isClientFiltered ? 1 : Math.ceil(total / limit);

  return (
    <DashboardPageLayout
      signedOut={{
        message: "Sign in to view Git Project issues.",
        forceRedirectUrl: "/git-projects/issues",
        signUpForceRedirectUrl: "/git-projects/issues",
      }}
      title="Git Project Issues"
      description={`${visibleTotal} issue${visibleTotal === 1 ? "" : "s"} from repositories tracked by Pipeline.`}
      stickyHeader
    >
      <ForgejoIssueFilters
        stateFilter={stateFilter}
        onStateChange={(v) => {
          setStateFilter(v);
          setStaleOnly(false);
          setRecentClosedOnly(false);
          setPage(1);
        }}
        repoFilter={repoFilter}
        onRepoChange={(v) => {
          setRepoFilter(v);
          setPage(1);
        }}
        search={search}
        onSearchChange={(v) => {
          setSearch(v);
          setPage(1);
        }}
        repos={repos}
      />

      {staleOnly ? (
        <div className="mb-4 flex flex-col gap-3 rounded-xl border border-[color:var(--warning)]/35 bg-[color:rgba(251,191,36,0.12)] p-3 text-sm text-[color:var(--warning)] sm:flex-row sm:items-center sm:justify-between">
          <span>
            Showing open issues not updated in {STALE_ISSUE_DAYS}+ days.
          </span>
          <Button
            type="button"
            variant="ghost"
            size="sm"
            className="w-full text-[color:var(--warning)] hover:bg-[color:rgba(251,191,36,0.14)] sm:w-auto"
            onClick={() => {
              setStaleOnly(false);
              setPage(1);
            }}
          >
            Show All Open Issues
          </Button>
        </div>
      ) : null}

      {recentClosedOnly ? (
        <div className="mb-4 flex flex-col gap-3 rounded-xl border border-[color:var(--success)]/35 bg-[color:rgba(52,211,153,0.12)] p-3 text-sm text-[color:var(--success)] sm:flex-row sm:items-center sm:justify-between">
          <span>
            Showing issues closed in the last {RECENT_CLOSED_DAYS} days.
          </span>
          <Button
            type="button"
            variant="ghost"
            size="sm"
            className="w-full text-[color:var(--success)] hover:bg-[color:rgba(52,211,153,0.14)] sm:w-auto"
            onClick={() => {
              setRecentClosedOnly(false);
              setPage(1);
            }}
          >
            Show All Closed Issues
          </Button>
        </div>
      ) : null}

      {error ? (
        <div className="mb-4 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}

      <ForgejoIssuesTable
        issues={visibleIssues}
        repositories={repos}
        isLoading={isLoadingIssues}
        canClose={canCloseIssues}
        onRefresh={handleRefresh}
      />

      {totalPages > 1 && (
        <div className="mt-4 flex flex-col gap-3 text-sm text-muted sm:flex-row sm:items-center sm:justify-between">
          <span className="break-words">
            Page {page} of {totalPages} ({visibleTotal} total)
          </span>
          <div className="flex gap-2">
            <Button
              variant="outline"
              size="sm"
              onClick={() => setPage((p) => Math.max(1, p - 1))}
              disabled={page <= 1}
            >
              Previous
            </Button>
            <Button
              variant="outline"
              size="sm"
              onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
              disabled={page >= totalPages}
            >
              Next
            </Button>
          </div>
        </div>
      )}
    </DashboardPageLayout>
  );
}
