import { CheckIcon, PlusIcon } from "@heroicons/react/20/solid";
import { BookOpenIcon } from "@heroicons/react/24/solid";
import { useSearchParams } from "@remix-run/react";
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { useCallback } from "react";
import { SearchInput } from "~/components/primitives/SearchInput";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons";
import { V4Title } from "~/components/V4Badge";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import { Badge } from "~/components/primitives/Badge";
import { Button, LinkButton } from "~/components/primitives/Buttons";
import { CopyableText } from "~/components/primitives/CopyableText";
import { DateTime } from "~/components/primitives/DateTime";
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components/primitives/Dialog";
import { Header3 } from "~/components/primitives/Headers";
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
import { PaginationControls } from "~/components/primitives/Pagination";
import { Paragraph } from "~/components/primitives/Paragraph";
import { PopoverMenuItem } from "~/components/primitives/Popover";
import * as Property from "~/components/primitives/PropertyTable";
import { Switch } from "~/components/primitives/Switch";
import {
  Table,
  TableBlankRow,
  TableBody,
  TableCell,
  TableCellMenu,
  TableHeader,
  TableHeaderCell,
  TableRow,
} from "~/components/primitives/Table";
import { InfoIconTooltip, SimpleTooltip } from "~/components/primitives/Tooltip";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";

import { BranchesPresenter } from "~/presenters/v3/BranchesPresenter.server";
import { logger } from "~/services/logger.server";
import { requireUserId } from "~/services/session.server";
import { cn } from "~/utils/cn";
import { branchesDevPath, docsPath, ProjectParamSchema } from "~/utils/pathBuilder";
import { ArchiveButton } from "../resources.branches.archive";
import { NewBranchPanel } from "~/routes/resources.branches.create";
import { BranchesOptions } from "~/utils/branches";
import { IconArrowBearRight2 } from "@tabler/icons-react";
import { useAutoRevalidate } from "~/hooks/useAutoRevalidate";

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
  const userId = await requireUserId(request);
  const { projectParam } = ProjectParamSchema.parse(params);

  const searchParams = new URL(request.url).searchParams;
  const parsedSearchParams = BranchesOptions.safeParse(Object.fromEntries(searchParams));
  const options = parsedSearchParams.success ? parsedSearchParams.data : {};

  try {
    const presenter = new BranchesPresenter();
    const result = await presenter.call({
      userId,
      projectSlug: projectParam,
      env: "development",
      ...options,
    });

    return typedjson(result);
  } catch (error) {
    logger.error("Error loading dev branches page", { error });
    throw new Response(undefined, {
      status: 400,
      statusText: "Something went wrong, if this problem persists please contact support.",
    });
  }
};

export default function Page() {
  const { branches, limits, currentPage, totalPages } = useTypedLoaderData<typeof loader>();
  useAutoRevalidate({ interval: 5000 });

  const organization = useOrganization();
  const project = useProject();
  const environment = useEnvironment();

  const atBranchLimit = limits.used >= limits.limit;
  const usageRatio = limits.limit > 0 ? Math.min(limits.used / limits.limit, 1) : 0;

  return (
    <PageContainer>
      <NavBar>
        <PageTitle title={<V4Title>Dev branches</V4Title>} />
        <PageAccessories>
          <AdminDebugTooltip>
            <Property.Table>
              {branches.map((branch) => (
                <Property.Item key={branch.id}>
                  <Property.Label>{branch.branchName}</Property.Label>
                  <Property.Value>{branch.id}</Property.Value>
                </Property.Item>
              ))}
            </Property.Table>
          </AdminDebugTooltip>

          <LinkButton
            variant={"docs/small"}
            LeadingIcon={BookOpenIcon}
            to={docsPath("deployment/dev-branches")}
          >
            Dev branches docs
          </LinkButton>

          {limits.isAtLimit ? (
            <BranchLimitReachedDialog limits={limits} />
          ) : (
            <NewBranchPanel
              button={
                <Button
                  variant="primary/small"
                  shortcut={{ key: "n" }}
                  LeadingIcon={PlusIcon}
                  leadingIconClassName="text-white"
                  fullWidth
                  textAlignLeft
                >
                  New branch…
                </Button>
              }
              env="development"
            />
          )}
        </PageAccessories>
      </NavBar>
      <PageBody scrollable={false}>
        <div className="grid max-h-full min-h-full grid-rows-[auto_1fr_auto]">
          <div className="flex items-center justify-between gap-x-1.5 p-2">
            <BranchFilters />
            <PaginationControls
              currentPage={currentPage}
              totalPages={totalPages}
              showPageNumbers={false}
            />
          </div>

          <div className="grid max-h-full min-h-full grid-rows-[1fr] overflow-x-auto">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHeaderCell>Branch</TableHeaderCell>
                  <TableHeaderCell>Created</TableHeaderCell>
                  <TableHeaderCell>Last active</TableHeaderCell>
                  <TableHeaderCell>Archived</TableHeaderCell>
                  <TableHeaderCell>
                    <span className="sr-only">Actions</span>
                  </TableHeaderCell>
                </TableRow>
              </TableHeader>
              <TableBody>
                {branches.length === 0 ? (
                  <TableBlankRow colSpan={5}>
                    <Paragraph>There are no matches for your filters</Paragraph>
                  </TableBlankRow>
                ) : (
                  branches.map((branch) => {
                    const path = branchesDevPath(organization, project, branch);
                    const cellClass = branch.archivedAt ? "opacity-50" : "";
                    const isSelected = branch.id === environment.id;

                    return (
                      <TableRow key={branch.id}>
                        <TableCell isTabbableCell className={cellClass}>
                          <div className="flex items-center gap-1">
                            <BranchEnvironmentIconSmall
                              className={cn("size-4", isSelected && "text-dev")}
                            />
                            <CopyableText
                              value={branch.branchName}
                              className={cn(isSelected && "text-dev")}
                            />
                            {isSelected && <Badge variant="extra-small">Current</Badge>}
                          </div>
                        </TableCell>
                        <TableCell className={cellClass}>
                          <DateTime date={branch.createdAt} />
                        </TableCell>
                        <TableCell className={cellClass}>
                          {branch.isConnected ? (
                            <>Online now</>
                          ) : branch.lastActivity ? (
                            <DateTime date={branch.lastActivity} />
                          ) : null}
                        </TableCell>
                        <TableCell className={cellClass}>
                          {branch.archivedAt ? (
                            <CheckIcon className="size-4 text-text-dimmed" />
                          ) : (
                            "–"
                          )}
                        </TableCell>
                        <TableCellMenu
                          className="pl-32"
                          isSticky
                          hiddenButtons={
                            isSelected ? null : (
                              <LinkButton
                                to={path}
                                variant="secondary/small"
                                LeadingIcon={IconArrowBearRight2}
                                leadingIconClassName="text-blue-500 -mr-2"
                                className="pl-1.5"
                              >
                                Switch to branch
                              </LinkButton>
                            )
                          }
                          popoverContent={
                            !isSelected || !branch.archivedAt ? (
                              <>
                                {isSelected ? null : (
                                  <PopoverMenuItem
                                    to={path}
                                    icon={IconArrowBearRight2}
                                    leadingIconClassName="text-blue-500 -mr-0.5 -ml-1"
                                    title="Switch to branch"
                                  />
                                )}
                                {!branch.archivedAt ? (
                                  <ArchiveButton
                                    environment={branch}
                                    // The root dev env (no parent) is the default
                                    // branch and can't be archived — matches the
                                    // guard in ArchiveBranchService.
                                    disabled={!branch.parentEnvironmentId}
                                  />
                                ) : null}
                              </>
                            ) : null
                          }
                        />
                      </TableRow>
                    );
                  })
                )}
              </TableBody>
            </Table>
          </div>

          <div className="flex w-full items-start justify-between">
            <div className="flex h-fit w-full items-center gap-4 border-t border-grid-bright bg-background-bright p-[0.86rem] pl-4">
              <SimpleTooltip
                button={
                  <div className="size-6">
                    <svg className="h-full w-full -rotate-90 overflow-visible">
                      <circle
                        className="fill-none stroke-grid-bright"
                        strokeWidth="4"
                        r="10"
                        cx="12"
                        cy="12"
                      />
                      <circle
                        className={`fill-none ${atBranchLimit ? "stroke-error" : "stroke-success"}`}
                        strokeWidth="4"
                        r="10"
                        cx="12"
                        cy="12"
                        strokeDasharray={`${usageRatio * 62.8} 62.8`}
                        strokeDashoffset="0"
                        strokeLinecap="round"
                      />
                    </svg>
                  </div>
                }
                content={`${Math.round(usageRatio * 100)}%`}
              />
              <div className="flex w-full items-center justify-between gap-6">
                {atBranchLimit ? (
                  <Header3 className="text-error">
                    You've used all {limits.limit} of your branches. Archive one to free up space.
                  </Header3>
                ) : (
                  <div className="flex items-center gap-1">
                    <Header3>
                      You've used {limits.used}/{limits.limit} of your branches
                    </Header3>
                    <InfoIconTooltip content="Archived branches don't count towards your limit." />
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>
      </PageBody>
    </PageContainer>
  );
}

export function BranchFilters() {
  const [searchParams, setSearchParams] = useSearchParams();
  const { showArchived } = BranchesOptions.parse(Object.fromEntries(searchParams.entries()));

  const handleArchivedChange = useCallback((checked: boolean) => {
    setSearchParams((s) => {
      if (checked) {
        s.set("showArchived", "true");
      } else {
        s.delete("showArchived");
      }
      s.delete("page");
      return s;
    });
  }, []);

  return (
    <div className="flex w-full items-center justify-between gap-2">
      <SearchInput placeholder="Search branch name…" resetParams={["page"]} />
      <Switch
        checked={showArchived ?? false}
        onCheckedChange={handleArchivedChange}
        label="Show archived"
        variant="secondary/small"
      />
    </div>
  );
}

function BranchLimitReachedDialog({
  limits,
}: {
  limits: {
    used: number;
    limit: number;
  };
}) {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button
          LeadingIcon={PlusIcon}
          leadingIconClassName="text-white"
          variant="primary/small"
          shortcut={{ key: "n" }}
        >
          New branch
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>You've exceeded your limit</DialogHeader>
        <div className="mt-2">
          <Paragraph spacing>
            You've used {limits.used}/{limits.limit} of your branches.
          </Paragraph>
          <Paragraph>You can archive a branch to free up space.</Paragraph>
        </div>
      </DialogContent>
    </Dialog>
  );
}
