import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { parseWithZod } from "@conform-to/zod";
import { ArrowUpCircleIcon, CheckIcon, EnvelopeIcon, PlusIcon } from "@heroicons/react/20/solid";
import { BookOpenIcon } from "@heroicons/react/24/solid";
import { DialogClose } from "@radix-ui/react-dialog";
import { useFetcher, useSearchParams } from "@remix-run/react";
import { type ActionFunctionArgs, json, type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { tryCatch } from "@trigger.dev/core/v3";
import { useCallback, useEffect, useState } from "react";
import { SearchInput } from "~/components/primitives/SearchInput";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons";
import { BranchesNoBranchableEnvironment, BranchesNoBranches } from "~/components/BlankStatePanels";
import { Feedback } from "~/components/Feedback";
import { GitMetadata } from "~/components/GitMetadata";
import { V4Title } from "~/components/V4Badge";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { MainCenteredContainer, 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,
  DialogFooter,
  DialogHeader,
  DialogTrigger,
} from "~/components/primitives/Dialog";
import { Fieldset } from "~/components/primitives/Fieldset";
import { FormButtons } from "~/components/primitives/FormButtons";
import { FormError } from "~/components/primitives/FormError";
import { Header3 } from "~/components/primitives/Headers";
import { InputGroup } from "~/components/primitives/InputGroup";
import { InputNumberStepper } from "~/components/primitives/InputNumberStepper";
import { Label } from "~/components/primitives/Label";
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 { SpinnerWhite } from "~/components/primitives/Spinner";
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 { useShowSelfServe } from "~/hooks/useShowSelfServe";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";

import { findProjectBySlug } from "~/models/project.server";
import { redirectWithErrorMessage } from "~/models/message.server";
import { BranchesPresenter } from "~/presenters/v3/BranchesPresenter.server";
import { logger } from "~/services/logger.server";
import { getCurrentPlan, getSelfServePurchaseBlockReason } from "~/services/platform.v3.server";
import { requireUserId } from "~/services/session.server";
import { cn } from "~/utils/cn";
import {
  branchesPath,
  docsPath,
  EnvironmentParamSchema,
  ProjectParamSchema,
  v3BillingPath,
} from "~/utils/pathBuilder";
import { formatCurrency, formatNumber } from "~/utils/numberFormatter";
import { SetBranchesAddOnService } from "~/v3/services/setBranchesAddOn.server";
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
import { ArchiveButton } from "../resources.branches.archive";
import { NewBranchPanel } from "~/routes/resources.branches.create";
import { BranchesOptions } from "~/utils/branches";
import { IconArrowBearRight2 } from "@tabler/icons-react";

const PurchaseSchema = z.discriminatedUnion("action", [
  z.object({
    action: z.literal("purchase"),
    amount: z.coerce.number().int("Must be a whole number").min(0, "Amount must be 0 or more"),
  }),
  z.object({
    action: z.literal("quota-increase"),
    amount: z.coerce.number().int("Must be a whole number").min(1, "Amount must be greater than 0"),
  }),
]);

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: "preview",
      ...options,
    });

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

export async function action({ request, params }: ActionFunctionArgs) {
  const userId = await requireUserId(request);

  const formData = await request.formData();
  const formType = formData.get("_formType");

  if (formType === "purchase-branches") {
    const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);
    const project = await findProjectBySlug(organizationSlug, projectParam, userId);
    const redirectPath = branchesPath(
      { slug: organizationSlug },
      { slug: projectParam },
      { slug: envParam }
    );

    if (!project) {
      throw await redirectWithErrorMessage(redirectPath, request, "Project not found");
    }

    const currentPlan = await getCurrentPlan(project.organizationId);
    const purchaseBlockReason = getSelfServePurchaseBlockReason(currentPlan);
    if (purchaseBlockReason === "plan_unavailable") {
      return json(
        { ok: false, error: "Unable to verify billing status. Please try again." } as const,
        { status: 503 }
      );
    }
    if (purchaseBlockReason === "managed_billing") {
      return json({ ok: false, error: "Contact us to request more branches." } as const, {
        status: 403,
      });
    }

    const submission = parseWithZod(formData, { schema: PurchaseSchema });

    if (submission.status !== "success") {
      return json(submission.reply());
    }

    const service = new SetBranchesAddOnService();
    const [error, result] = await tryCatch(
      service.call({
        userId,
        organizationId: project.organizationId,
        action: submission.value.action,
        amount: submission.value.amount,
      })
    );

    if (error) {
      return json(
        submission.reply({
          fieldErrors: { amount: [error instanceof Error ? error.message : "Unknown error"] },
        })
      );
    }

    if (!result.success) {
      return json(submission.reply({ fieldErrors: { amount: [result.error] } }));
    }

    return json({ ok: true } as const);
  }

  // Branch creation is handled by the `resources.branches.create` resource
  // route; this action only services the purchase flow above.
  return json({ ok: false, error: "Unsupported action" } as const, { status: 400 });
}

export default function Page() {
  const {
    branchableEnvironment,
    branches,
    hasFilters: _hasFilters,
    limits,
    currentPage,
    totalPages,
    hasBranches,
    canPurchaseBranches,
    extraBranches,
    branchPricing,
    maxBranchQuota,
    planBranchLimit,
  } = useTypedLoaderData<typeof loader>();
  const organization = useOrganization();
  const project = useProject();
  const environment = useEnvironment();

  const plan = useCurrentPlan();
  const showSelfServe = useShowSelfServe();
  const requiresUpgrade =
    plan?.v3Subscription?.plan &&
    limits.used >= plan.v3Subscription.plan.limits.branches.number &&
    !plan.v3Subscription.plan.limits.branches.canExceed;
  const canUpgrade =
    plan?.v3Subscription?.plan && !plan.v3Subscription.plan.limits.branches.canExceed;
  const atBranchLimit = limits.used >= limits.limit;
  const usageRatio = limits.limit > 0 ? Math.min(limits.used / limits.limit, 1) : 0;

  if (!branchableEnvironment) {
    return (
      <PageContainer>
        <NavBar>
          <PageTitle title={<V4Title>Preview branches</V4Title>} />
        </NavBar>
        <PageBody>
          <MainCenteredContainer className="max-w-md">
            <BranchesNoBranchableEnvironment showSelfServe={showSelfServe} />
          </MainCenteredContainer>
        </PageBody>
      </PageContainer>
    );
  }

  return (
    <PageContainer>
      <NavBar>
        <PageTitle title={<V4Title>Preview 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/preview-branches")}
          >
            Branches docs
          </LinkButton>

          {limits.isAtLimit ? (
            <UpgradePanel
              limits={limits}
              canUpgrade={canUpgrade ?? false}
              canPurchaseBranches={canPurchaseBranches}
              branchPricing={branchPricing}
              extraBranches={extraBranches}
              maxBranchQuota={maxBranchQuota}
              planBranchLimit={planBranchLimit}
            />
          ) : (
            <NewBranchPanel
              button={
                <Button
                  variant="primary/small"
                  shortcut={{ key: "n" }}
                  LeadingIcon={PlusIcon}
                  leadingIconClassName="text-white"
                  fullWidth
                  textAlignLeft
                >
                  New branch…
                </Button>
              }
              env="preview"
            />
          )}
        </PageAccessories>
      </NavBar>
      <PageBody scrollable={false}>
        <div className="grid max-h-full min-h-full grid-rows-[auto_1fr_auto]">
          {!hasBranches ? (
            <MainCenteredContainer className="max-w-md">
              <BranchesNoBranches
                env="preview"
                limits={limits}
                canUpgrade={canUpgrade ?? false}
                showSelfServe={showSelfServe}
              />
            </MainCenteredContainer>
          ) : (
            <>
              <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>Git</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 = branchesPath(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-preview")}
                                />
                                <CopyableText
                                  value={branch.branchName ?? ""}
                                  className={cn(isSelected && "text-preview")}
                                />
                                {isSelected && <Badge variant="extra-small">Current</Badge>}
                              </div>
                            </TableCell>
                            <TableCell className={cellClass}>
                              <DateTime date={branch.createdAt} />
                            </TableCell>
                            <TableCell className={cellClass}>
                              <div className="-ml-1 flex items-center">
                                <GitMetadata git={branch.git} />
                              </div>
                            </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} />
                                    ) : 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">
                    {requiresUpgrade ? (
                      <Header3 className="text-error">
                        You've used all {limits.limit} of your branches. Archive one or upgrade your
                        plan to enable more.
                      </Header3>
                    ) : (
                      <div className="flex items-center gap-1">
                        <Header3 className={atBranchLimit ? "text-error" : undefined}>
                          You've used {limits.used}/{limits.limit} of your branches
                        </Header3>
                        <InfoIconTooltip content="Archived branches don't count towards your limit." />
                      </div>
                    )}

                    {canPurchaseBranches && branchPricing ? (
                      <PurchaseBranchesModal
                        branchPricing={branchPricing}
                        extraBranches={extraBranches}
                        activeBranches={limits.used}
                        maxQuota={maxBranchQuota}
                        planBranchLimit={planBranchLimit}
                      />
                    ) : canUpgrade ? (
                      showSelfServe ? (
                        <div className="flex items-center gap-3">
                          <Paragraph variant="small" className="whitespace-nowrap text-text-dimmed">
                            Upgrade plan for more Preview Branches
                          </Paragraph>
                          <LinkButton
                            to={v3BillingPath(organization)}
                            variant="secondary/small"
                            LeadingIcon={ArrowUpCircleIcon}
                            leadingIconClassName="text-indigo-500"
                          >
                            Upgrade
                          </LinkButton>
                        </div>
                      ) : (
                        <Feedback
                          defaultValue="enterprise"
                          button={<Button variant="secondary/small">Request more</Button>}
                        />
                      )
                    ) : null}
                  </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 UpgradePanel({
  limits,
  canUpgrade,
  canPurchaseBranches,
  branchPricing,
  extraBranches,
  maxBranchQuota,
  planBranchLimit,
}: {
  limits: {
    used: number;
    limit: number;
  };
  canUpgrade: boolean;
  canPurchaseBranches: boolean;
  branchPricing: { stepSize: number; centsPerStep: number } | null;
  extraBranches: number;
  maxBranchQuota: number;
  planBranchLimit: number;
}) {
  const organization = useOrganization();
  const showSelfServe = useShowSelfServe();

  if (canPurchaseBranches && branchPricing) {
    return (
      <PurchaseBranchesModal
        branchPricing={branchPricing}
        extraBranches={extraBranches}
        activeBranches={limits.used}
        maxQuota={maxBranchQuota}
        planBranchLimit={planBranchLimit}
        triggerButton={
          <Button
            LeadingIcon={PlusIcon}
            leadingIconClassName="text-white"
            variant="primary/small"
            shortcut={{ key: "n" }}
          >
            Purchase more…
          </Button>
        }
      />
    );
  }

  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 one or upgrade your plan for more.</Paragraph>
        </div>
        <DialogFooter>
          {canUpgrade ? (
            showSelfServe ? (
              <LinkButton variant="primary/small" to={v3BillingPath(organization)}>
                Upgrade
              </LinkButton>
            ) : (
              <Feedback
                defaultValue="enterprise"
                button={<Button variant="secondary/small">Request more</Button>}
              />
            )
          ) : null}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function PurchaseBranchesModal({
  branchPricing,
  extraBranches,
  activeBranches,
  maxQuota,
  planBranchLimit,
  triggerButton,
}: {
  branchPricing: {
    stepSize: number;
    centsPerStep: number;
  };
  extraBranches: number;
  activeBranches: number;
  maxQuota: number;
  planBranchLimit: number;
  triggerButton?: React.ReactNode;
}) {
  const showSelfServe = useShowSelfServe();
  const fetcher = useFetcher();
  const lastSubmission =
    fetcher.data && typeof fetcher.data === "object" && "status" in fetcher.data
      ? fetcher.data
      : undefined;
  const [form, { amount }] = useForm({
    id: "purchase-branches",
    lastResult: lastSubmission as any,
    onValidate({ formData }) {
      return parseWithZod(formData, { schema: PurchaseSchema });
    },
    shouldRevalidate: "onSubmit",
  });

  const [amountValue, setAmountValue] = useState(extraBranches);
  useEffect(() => {
    setAmountValue(extraBranches);
  }, [extraBranches]);
  const isLoading = fetcher.state !== "idle";

  const [open, setOpen] = useState(false);
  useEffect(() => {
    const data = fetcher.data;
    if (
      fetcher.state === "idle" &&
      data !== null &&
      typeof data === "object" &&
      "ok" in data &&
      data.ok
    ) {
      setOpen(false);
    }
  }, [fetcher.state, fetcher.data]);

  const state = updateBranchState({
    value: amountValue,
    existingValue: extraBranches,
    quota: maxQuota,
    activeBranches,
    planBranchLimit,
  });
  const changeClassName =
    state === "decrease" ? "text-error" : state === "increase" ? "text-success" : undefined;

  const pricePerBranch = branchPricing.centsPerStep / branchPricing.stepSize / 100;
  const title = extraBranches === 0 ? "Purchase extra branches…" : "Add/remove extra branches…";

  if (!showSelfServe) {
    return (
      <Feedback
        defaultValue="enterprise"
        button={<Button variant="secondary/small">Request more</Button>}
      />
    );
  }

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        {triggerButton ?? (
          <Button variant="primary/small" onClick={() => setOpen(true)}>
            {title}
          </Button>
        )}
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>{title}</DialogHeader>
        <fetcher.Form method="post" {...getFormProps(form)}>
          <input type="hidden" name="_formType" value="purchase-branches" />
          <div className="flex flex-col gap-4 pt-2">
            <div className="flex flex-col gap-1">
              <Paragraph variant="small/bright">
                Purchase extra preview branches at {formatCurrency(pricePerBranch, false)}/month per
                branch. Reducing the number of branches will take effect at the start of the next
                billing cycle (1st of the month).
              </Paragraph>
            </div>
            <Fieldset>
              <InputGroup fullWidth>
                <Label htmlFor="amount" className="text-text-dimmed">
                  Total extra branches
                </Label>
                <InputNumberStepper
                  {...getInputProps(amount, { type: "number" })}
                  step={branchPricing.stepSize}
                  min={0}
                  max={undefined}
                  value={amountValue}
                  onChange={(e) => setAmountValue(Number(e.target.value))}
                  disabled={isLoading}
                />
                <FormError id={amount.errorId}>{amount.errors}</FormError>
                <FormError>{form.errors}</FormError>
              </InputGroup>
            </Fieldset>
            {state === "need_to_archive" ? (
              <div className="flex flex-col pb-3">
                <Paragraph variant="small" className="text-warning" spacing>
                  You need to archive{" "}
                  {formatNumber(activeBranches - (planBranchLimit + amountValue))} more{" "}
                  {activeBranches - (planBranchLimit + amountValue) === 1 ? "branch" : "branches"}{" "}
                  before you can reduce to this level.
                </Paragraph>
              </div>
            ) : state === "above_quota" ? (
              <div className="flex flex-col pb-3">
                <Paragraph variant="small" className="text-warning" spacing>
                  Currently you can only have up to {maxQuota} extra preview branches. Send a
                  request below to lift your current limit. We'll get back to you soon.
                </Paragraph>
              </div>
            ) : (
              <div className="flex flex-col pb-3 tabular-nums">
                <div className="grid grid-cols-2 border-b border-grid-dimmed pb-1">
                  <Header3 className="font-normal text-text-dimmed">Summary</Header3>
                  <Header3 className="justify-self-end font-normal text-text-dimmed">Total</Header3>
                </div>
                <div className="grid grid-cols-2 pt-2">
                  <Header3 className="pb-0 font-normal text-text-dimmed">
                    <span className="text-text-bright">{formatNumber(extraBranches)}</span> current
                    extra
                  </Header3>
                  <Header3 className="justify-self-end font-normal text-text-bright">
                    {formatCurrency(extraBranches * pricePerBranch, true)}
                  </Header3>
                </div>
                <div className="grid grid-cols-2 text-xs">
                  <span className="text-text-dimmed">
                    ({extraBranches} {extraBranches === 1 ? "branch" : "branches"})
                  </span>
                  <span className="justify-self-end text-text-dimmed">/mth</span>
                </div>
                <div className="grid grid-cols-2 pt-2">
                  <Header3 className={cn("pb-0 font-normal", changeClassName)}>
                    {state === "increase" ? "+" : null}
                    {formatNumber(amountValue - extraBranches)}
                  </Header3>
                  <Header3 className={cn("justify-self-end font-normal", changeClassName)}>
                    {state === "increase" ? "+" : null}
                    {formatCurrency((amountValue - extraBranches) * pricePerBranch, true)}
                  </Header3>
                </div>
                <div className="grid grid-cols-2 text-xs">
                  <span className="text-text-dimmed">
                    ({Math.abs(amountValue - extraBranches)}{" "}
                    {Math.abs(amountValue - extraBranches) === 1 ? "branch" : "branches"} @{" "}
                    {formatCurrency(pricePerBranch, true)}/mth)
                  </span>
                  <span className="justify-self-end text-text-dimmed">/mth</span>
                </div>
                <div className="grid grid-cols-2 pt-2">
                  <Header3 className="pb-0 font-normal text-text-dimmed">
                    <span className="text-text-bright">{formatNumber(amountValue)}</span> new total
                  </Header3>
                  <Header3 className="justify-self-end font-normal text-text-bright">
                    {formatCurrency(amountValue * pricePerBranch, true)}
                  </Header3>
                </div>
                <div className="grid grid-cols-2 text-xs">
                  <span className="text-text-dimmed">
                    ({amountValue} {amountValue === 1 ? "branch" : "branches"})
                  </span>
                  <span className="justify-self-end text-text-dimmed">/mth</span>
                </div>
              </div>
            )}
          </div>
          <FormButtons
            confirmButton={
              state === "above_quota" ? (
                <>
                  <input type="hidden" name="action" value="quota-increase" />
                  <Button
                    LeadingIcon={isLoading ? SpinnerWhite : EnvelopeIcon}
                    variant="primary/medium"
                    type="submit"
                    disabled={isLoading}
                  >
                    <span className="tabular-nums text-text-bright">{`Send request for ${formatNumber(
                      amountValue
                    )}`}</span>
                  </Button>
                </>
              ) : state === "decrease" || state === "need_to_archive" ? (
                <>
                  <input type="hidden" name="action" value="purchase" />
                  <Button
                    variant="danger/medium"
                    type="submit"
                    disabled={isLoading || state === "need_to_archive"}
                    LeadingIcon={isLoading ? SpinnerWhite : undefined}
                  >
                    <span className="tabular-nums text-text-bright">{`Remove ${formatNumber(
                      extraBranches - amountValue
                    )} ${extraBranches - amountValue === 1 ? "branch" : "branches"}`}</span>
                  </Button>
                </>
              ) : (
                <>
                  <input type="hidden" name="action" value="purchase" />
                  <Button
                    variant="primary/medium"
                    type="submit"
                    disabled={isLoading || state === "no_change"}
                    LeadingIcon={isLoading ? SpinnerWhite : undefined}
                  >
                    <span className="tabular-nums text-text-bright">{`Purchase ${formatNumber(
                      amountValue - extraBranches
                    )} ${amountValue - extraBranches === 1 ? "branch" : "branches"}`}</span>
                  </Button>
                </>
              )
            }
            cancelButton={
              <DialogClose asChild>
                <Button variant="secondary/medium" disabled={isLoading}>
                  Cancel
                </Button>
              </DialogClose>
            }
          />
        </fetcher.Form>
      </DialogContent>
    </Dialog>
  );
}

function updateBranchState({
  value,
  existingValue,
  quota,
  activeBranches,
  planBranchLimit,
}: {
  value: number;
  existingValue: number;
  quota: number;
  activeBranches: number;
  planBranchLimit: number;
}): "no_change" | "increase" | "decrease" | "above_quota" | "need_to_archive" {
  if (value === existingValue) return "no_change";
  if (value < existingValue) {
    const newTotalLimit = planBranchLimit + value;
    if (activeBranches > newTotalLimit) {
      return "need_to_archive";
    }
    return "decrease";
  }
  if (value > quota) return "above_quota";
  return "increase";
}
