import {
  ArrowRightIcon,
  ArrowUpCircleIcon,
  BookOpenIcon,
  ChatBubbleLeftEllipsisIcon,
  InformationCircleIcon,
  MapPinIcon,
} from "@heroicons/react/20/solid";
import { Form } from "@remix-run/react";
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { tryCatch } from "@trigger.dev/core";
import { useState } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { CloudProviderIcon } from "~/assets/icons/CloudProviderIcon";
import { FlagIcon } from "~/assets/icons/RegionIcons";
import { cloudProviderTitle } from "~/components/CloudProvider";
import { Feedback } from "~/components/Feedback";
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 { ClipboardField } from "~/components/primitives/ClipboardField";
import { CopyableText } from "~/components/primitives/CopyableText";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "~/components/primitives/Dialog";
import { InfoPanel } from "~/components/primitives/InfoPanel";
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
import { Paragraph } from "~/components/primitives/Paragraph";
import * as Property from "~/components/primitives/PropertyTable";
import {
  Table,
  TableBlankRow,
  TableBody,
  TableCell,
  TableCellMenu,
  TableHeader,
  TableHeaderCell,
  TableRow,
} from "~/components/primitives/Table";
import { TextLink } from "~/components/primitives/TextLink";
import { InfoIconTooltip } from "~/components/primitives/Tooltip";
import { useFeatures } from "~/hooks/useFeatures";
import { useOrganization } from "~/hooks/useOrganizations";
import { useHasAdminAccess } from "~/hooks/useUser";
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
import { findProjectBySlug } from "~/models/project.server";
import { type Region, RegionsPresenter } from "~/presenters/v3/RegionsPresenter.server";
import { requireUser } from "~/services/session.server";
import {
  docsPath,
  EnvironmentParamSchema,
  ProjectParamSchema,
  regionsPath,
  v3BillingPath,
} from "~/utils/pathBuilder";
import { SetDefaultRegionService } from "~/v3/services/setDefaultRegion.server";

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

  const presenter = new RegionsPresenter();
  const [error, result] = await tryCatch(
    presenter.call({
      userId: user.id,
      projectSlug: projectParam,
      isAdmin: user.admin || user.isImpersonating,
    })
  );

  if (error) {
    throw new Response(undefined, {
      status: 400,
      statusText: error.message,
    });
  }

  return typedjson(result);
};

const FormSchema = z.object({
  regionId: z.string(),
});

export const action = async ({ request, params }: ActionFunctionArgs) => {
  const user = await requireUser(request);
  const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);

  const project = await findProjectBySlug(organizationSlug, projectParam, user.id);

  const redirectPath = regionsPath(
    { slug: organizationSlug },
    { slug: projectParam },
    { slug: envParam }
  );

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

  const formData = await request.formData();
  const parsedFormData = FormSchema.safeParse(Object.fromEntries(formData));

  if (!parsedFormData.success) {
    throw await redirectWithErrorMessage(redirectPath, request, "No region specified");
  }

  const service = new SetDefaultRegionService();
  const [error, result] = await tryCatch(
    service.call({
      projectId: project.id,
      regionId: parsedFormData.data.regionId,
      isAdmin: user.admin || user.isImpersonating,
    })
  );

  if (error) {
    return redirectWithErrorMessage(redirectPath, request, error.message);
  }

  return redirectWithSuccessMessage(redirectPath, request, `Set ${result.name} as default`);
};

export default function Page() {
  const { regions, isPaying: _isPaying } = useTypedLoaderData<typeof loader>();
  const organization = useOrganization();
  const isAdmin = useHasAdminAccess();
  const { isManagedCloud } = useFeatures();

  return (
    <PageContainer>
      <NavBar>
        <PageTitle title="Regions" />
        <PageAccessories>
          <AdminDebugTooltip>
            <Property.Table>
              {regions.map((region) => (
                <Property.Item key={region.id}>
                  <Property.Label>{region.name}</Property.Label>
                  <Property.Value>{region.id}</Property.Value>
                </Property.Item>
              ))}
            </Property.Table>
          </AdminDebugTooltip>
        </PageAccessories>
      </NavBar>
      <PageBody scrollable={false}>
        <div className="grid max-h-full min-h-full grid-rows-[1fr]">
          {regions.length === 0 ? (
            <MainCenteredContainer className="max-w-md">
              <div className="text-center">
                <Paragraph>No regions found for this project.</Paragraph>
              </div>
            </MainCenteredContainer>
          ) : (
            <>
              <div className="overflow-x-auto">
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHeaderCell>Region</TableHeaderCell>
                      <TableHeaderCell>Cloud Provider</TableHeaderCell>
                      <TableHeaderCell>
                        <span className="flex items-center gap-1">
                          Location
                          <InfoIconTooltip
                            content="Region location is where your runs execute, not where your data is stored."
                            contentClassName="normal-case tracking-normal"
                          />
                        </span>
                      </TableHeaderCell>
                      <TableHeaderCell>Static IPs</TableHeaderCell>
                      {isAdmin && <TableHeaderCell>Admin</TableHeaderCell>}
                      <TableHeaderCell
                        alignment="right"
                        tooltip={
                          <div className="max-w-48">
                            <Paragraph variant="small">
                              When you trigger a run it will execute in your default region, unless
                              you override the region when triggering.
                            </Paragraph>
                            <LinkButton
                              variant="docs/small"
                              LeadingIcon={BookOpenIcon}
                              to={docsPath("triggering#region")}
                              className="mb-1 mt-3"
                            >
                              Read docs
                            </LinkButton>
                          </div>
                        }
                      >
                        Default region
                      </TableHeaderCell>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {regions.length === 0 ? (
                      <TableBlankRow colSpan={5}>
                        <Paragraph>There are no regions for this project</Paragraph>
                      </TableBlankRow>
                    ) : (
                      regions.map((region) => {
                        return (
                          <TableRow key={region.id}>
                            <TableCell isTabbableCell>
                              <span className="flex items-center gap-2">
                                <CopyableText value={region.name} />
                                {region.workloadType === "MICROVM" && (
                                  <Badge variant="small">MicroVM</Badge>
                                )}
                              </span>
                            </TableCell>
                            <TableCell>
                              {region.cloudProvider ? (
                                <span className="flex items-center gap-2">
                                  <CloudProviderIcon
                                    provider={region.cloudProvider}
                                    className="size-6"
                                  />
                                  {cloudProviderTitle(region.cloudProvider)}
                                </span>
                              ) : (
                                "–"
                              )}
                            </TableCell>
                            <TableCell>
                              <span className="flex items-center gap-2">
                                {region.location ? (
                                  <FlagIcon region={region.location} className="size-5" />
                                ) : null}
                                {region.description ?? "–"}
                              </span>
                            </TableCell>
                            <TableCell>
                              {region.staticIPs === null ? (
                                <LinkButton
                                  variant="secondary/small"
                                  to={v3BillingPath(
                                    organization,
                                    "Upgrade your plan to unlock static IPs"
                                  )}
                                  LeadingIcon={ArrowUpCircleIcon}
                                  leadingIconClassName="text-indigo-500"
                                >
                                  Unlock static IPs
                                </LinkButton>
                              ) : region.staticIPs !== undefined ? (
                                <ClipboardField
                                  value={region.staticIPs}
                                  variant={"secondary/small"}
                                />
                              ) : (
                                "Not available"
                              )}
                            </TableCell>
                            {isAdmin && (
                              <TableCell>{region.isHidden ? "Hidden" : "Visible"}</TableCell>
                            )}
                            {region.isDefault ? (
                              <TableCell alignment="right">
                                <Badge variant="small" className="inline-grid">
                                  Default
                                </Badge>
                              </TableCell>
                            ) : (
                              <TableCellMenu
                                className="pl-32"
                                isSticky
                                hiddenButtons={
                                  <SetDefaultDialog regions={regions} newDefaultRegion={region} />
                                }
                              />
                            )}
                          </TableRow>
                        );
                      })
                    )}

                    <TableRow className="h-12.5">
                      <TableCell colSpan={isAdmin ? 5 : 4}>
                        <Paragraph variant="extra-small">Suggest a new region</Paragraph>
                      </TableCell>
                      <TableCellMenu
                        alignment="right"
                        isSticky
                        visibleButtons={
                          <Feedback
                            button={
                              <Button
                                variant="secondary/small"
                                LeadingIcon={ChatBubbleLeftEllipsisIcon}
                                leadingIconClassName="text-indigo-500"
                              >
                                Suggest a region…
                              </Button>
                            }
                            defaultValue="region"
                          />
                        }
                      />
                    </TableRow>
                  </TableBody>
                </Table>
                {isManagedCloud && (
                  <InfoPanel
                    icon={InformationCircleIcon}
                    iconClassName="size-4"
                    variant="minimal"
                    panelClassName="max-w-full gap-1"
                  >
                    <Paragraph variant="extra-small">
                      Trigger.dev is fully{" "}
                      <TextLink to="https://security.trigger.dev/gdpr?tab=securityControls&frameworks=gdpr_v1">
                        GDPR compliant
                      </TextLink>
                      . Learn more in our{" "}
                      <TextLink to="https://security.trigger.dev">security portal</TextLink> or{" "}
                      <Feedback
                        button={
                          <span className="cursor-pointer text-xs text-indigo-500 transition hover:text-indigo-400">
                            get in touch
                          </span>
                        }
                        defaultValue="feedback"
                      />
                      .
                    </Paragraph>
                  </InfoPanel>
                )}
              </div>
            </>
          )}
        </div>
      </PageBody>
    </PageContainer>
  );
}

function SetDefaultDialog({
  regions,
  newDefaultRegion,
}: {
  regions: Region[];
  newDefaultRegion: Region;
}) {
  const [isOpen, setIsOpen] = useState(false);
  const { isManagedCloud } = useFeatures();
  const currentDefaultRegion = regions.find((r) => r.isDefault);

  return (
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
      <DialogTrigger asChild>
        <Button
          variant="minimal/small"
          LeadingIcon={MapPinIcon}
          leadingIconClassName="text-blue-500"
          iconSpacing="gap-2"
          className="pl-2"
        >
          <span className="text-text-bright">Set as default…</span>
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Set as default region</DialogTitle>
        </DialogHeader>
        <DialogDescription asChild>
          <div>
            <Paragraph>
              Are you sure you want to set {newDefaultRegion.name} as your new default region?
            </Paragraph>

            <div className="my-4 flex">
              <div className="flex flex-1 flex-col rounded-md border border-grid-dimmed">
                <div className="border-b border-grid-dimmed bg-background-bright p-3 font-medium">
                  <Paragraph variant="small/bright">Current default</Paragraph>
                </div>
                <div className="border-b border-grid-dimmed p-3">
                  <Paragraph variant="small">{currentDefaultRegion?.name ?? "–"}</Paragraph>
                </div>
                <div className="border-b border-grid-dimmed p-3">
                  <Paragraph variant="small" className="flex items-center gap-2">
                    {currentDefaultRegion?.cloudProvider ? (
                      <>
                        <CloudProviderIcon
                          provider={currentDefaultRegion.cloudProvider}
                          className="size-6"
                        />
                        {cloudProviderTitle(currentDefaultRegion.cloudProvider)}
                      </>
                    ) : (
                      "–"
                    )}
                  </Paragraph>
                </div>
                <div className="p-3">
                  <Paragraph variant="small" className="flex items-center gap-2">
                    {currentDefaultRegion?.location ? (
                      <FlagIcon region={currentDefaultRegion.location} className="size-5" />
                    ) : null}
                    {currentDefaultRegion?.description ?? "–"}
                  </Paragraph>
                </div>
              </div>

              {/* Middle column with arrow */}
              <div className="flex items-center justify-center px-3">
                <div className="flex size-10 items-center justify-center rounded-full border border-grid-dimmed bg-background-bright p-2">
                  <ArrowRightIcon className="size-4 text-text-dimmed" />
                </div>
              </div>

              {/* Right column */}
              <div className="flex flex-1 flex-col rounded-md border border-grid-dimmed">
                <div className="border-b border-grid-dimmed bg-background-bright p-3 font-medium">
                  <Paragraph variant="small/bright">New default</Paragraph>
                </div>
                <div className="border-b border-grid-dimmed p-3">
                  <Paragraph variant="small">{newDefaultRegion.name}</Paragraph>
                </div>
                <div className="border-b border-grid-dimmed p-3">
                  <Paragraph variant="small" className="flex items-center gap-2">
                    {newDefaultRegion.cloudProvider ? (
                      <>
                        <CloudProviderIcon
                          provider={newDefaultRegion.cloudProvider}
                          className="size-6"
                        />
                        {cloudProviderTitle(newDefaultRegion.cloudProvider)}
                      </>
                    ) : (
                      "–"
                    )}
                  </Paragraph>
                </div>
                <div className="p-3">
                  <Paragraph variant="small" className="flex items-center gap-2">
                    {newDefaultRegion.location ? (
                      <FlagIcon region={newDefaultRegion.location} className="size-5" />
                    ) : null}
                    {newDefaultRegion.description ?? "–"}
                  </Paragraph>
                </div>
              </div>
            </div>

            <Paragraph>
              Runs triggered from now on will execute in "{newDefaultRegion.name}", unless you{" "}
              <TextLink to={docsPath("triggering#region")}>override when triggering</TextLink>.
            </Paragraph>

            <InfoPanel
              icon={InformationCircleIcon}
              iconClassName="size-4"
              variant="minimal"
              panelClassName="mt-4 max-w-full gap-1 border-t border-grid-dimmed pt-4 pb-0 pl-0"
            >
              <Paragraph variant="extra-small">
                Region is where your runs execute, not where your data is stored.
                {isManagedCloud ? (
                  <>
                    {" "}
                    Trigger.dev is fully{" "}
                    <TextLink to="https://security.trigger.dev/gdpr?tab=securityControls&frameworks=gdpr_v1">
                      GDPR compliant
                    </TextLink>
                    .
                  </>
                ) : null}
              </Paragraph>
            </InfoPanel>
          </div>
        </DialogDescription>
        <DialogFooter>
          <Button variant="secondary/medium" onClick={() => setIsOpen(false)}>
            Cancel
          </Button>
          <Form method="post">
            <Button
              variant="primary/medium"
              type="submit"
              name="regionId"
              shortcut={{ modifiers: ["mod"], key: "enter" }}
              value={newDefaultRegion.id}
            >
              Set as default
            </Button>
          </Form>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
