import {
  useActionMutation,
  useActionQuery,
} from "@agent-native/core/client/hooks";
import { useOrgRole } from "@agent-native/core/client/org";
import {
  IconCheck,
  IconLoader2,
  IconRefresh,
  IconSettings,
} from "@tabler/icons-react";
import { useMemo, useState, type ReactNode } from "react";
import { toast } from "sonner";

import { ActionQueryError } from "./action-query-error";
import { Button } from "./ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Skeleton } from "./ui/skeleton";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";

interface VaultSecret {
  id: string;
  name?: string | null;
  credentialKey: string;
  provider?: string | null;
  description?: string | null;
}

interface VaultGrant {
  id: string;
  secretId: string;
  appId: string;
  status?: string | null;
}

interface AppKeysPopoverProps {
  appId: string;
  appName: string;
  trigger?: ReactNode;
  align?: "start" | "center" | "end";
  side?: "top" | "right" | "bottom" | "left";
}

export function AppKeysPopover({
  appId,
  appName,
  trigger,
  align = "end",
  side = "bottom",
}: AppKeysPopoverProps) {
  const [open, setOpen] = useState(false);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <Tooltip>
        <TooltipTrigger asChild>
          <PopoverTrigger asChild>
            {trigger ?? (
              <Button
                type="button"
                variant="ghost"
                size="sm"
                aria-label={`Manage keys for ${appName}`}
                onClick={(event) => {
                  // Keep parent card click handlers from also firing. Do not
                  // preventDefault here: Radix uses the same click to open the
                  // popover trigger.
                  event.stopPropagation();
                }}
                className="size-7 rounded-md p-0 text-muted-foreground transition-[background-color,color] hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:bg-accent data-[state=open]:text-foreground"
              >
                <IconSettings size={14} />
              </Button>
            )}
          </PopoverTrigger>
        </TooltipTrigger>
        <TooltipContent>Manage keys</TooltipContent>
      </Tooltip>
      <PopoverContent
        align={align}
        side={side}
        sideOffset={6}
        className="w-80 p-3"
        onClick={(event) => event.stopPropagation()}
      >
        {open ? <AppKeysPanel appId={appId} appName={appName} /> : null}
      </PopoverContent>
    </Popover>
  );
}

export function AppKeysPanel({
  appId,
  appName,
}: {
  appId: string;
  appName: string;
}) {
  const { org, role, isLoading: orgLoading, error: orgError } = useOrgRole();
  const accessReady = !orgLoading && !orgError && !!org;
  const canManageVault =
    accessReady && (!org.orgId || role === "owner" || role === "admin");
  const secretsQuery = useActionQuery("list-vault-secret-options", {});
  const grantsQuery = useActionQuery(
    "list-vault-grants",
    { appId },
    { enabled: canManageVault },
  );
  const accessQuery = useActionQuery("get-vault-access-settings", {});
  const { data: secrets = [], isLoading: secretsLoading } = secretsQuery;
  const {
    data: grants = [],
    isLoading: grantsLoading,
    refetch: refetchGrants,
  } = grantsQuery;
  const { data: accessSettings, isLoading: accessLoading } = accessQuery;
  const accessMode =
    (accessSettings as any)?.mode === "manual" ? "manual" : "all-apps";

  const grantBySecretId = useMemo(() => {
    const map = new Map<string, VaultGrant>();
    for (const grant of grants as VaultGrant[]) {
      if (grant.status && grant.status !== "active") continue;
      map.set(grant.secretId, grant);
    }
    return map;
  }, [grants]);

  // Track per-secret pending state so a fast double-click on the same row
  // can't queue two `create-vault-grant` requests (which would silently
  // create duplicate active grants — a later revoke only clears one).
  const [pendingSecretIds, setPendingSecretIds] = useState<Set<string>>(
    () => new Set(),
  );
  const markPending = (secretId: string, pending: boolean) =>
    setPendingSecretIds((prev) => {
      const next = new Set(prev);
      if (pending) next.add(secretId);
      else next.delete(secretId);
      return next;
    });

  const grantMutation = useActionMutation("create-vault-grant", {
    onSuccess: () => refetchGrants(),
    onError: (err) => toast.error(`Could not grant: ${String(err)}`),
  });

  const revokeMutation = useActionMutation("revoke-vault-grant", {
    onSuccess: () => refetchGrants(),
    onError: (err) => toast.error(`Could not revoke: ${String(err)}`),
  });

  const syncMutation = useActionMutation("sync-vault-to-app", {
    onSuccess: (result: any) => {
      const synced = result?.synced ?? 0;
      toast.success(
        synced > 0
          ? `Synced ${synced} key${synced === 1 ? "" : "s"} to ${appName}`
          : `${appName} is up to date`,
      );
    },
    onError: (err) => toast.error(`Sync failed: ${String(err)}`),
  });

  const isLoading =
    orgLoading || secretsLoading || grantsLoading || accessLoading;
  const error =
    secretsQuery.error ?? grantsQuery.error ?? accessQuery.error ?? orgError;
  const grantedCount = grantBySecretId.size;
  const typedSecrets = secrets as VaultSecret[];
  const allApps = accessMode !== "manual";

  const toggleSecret = (secret: VaultSecret) => {
    if (!canManageVault || allApps) return;
    if (pendingSecretIds.has(secret.id)) return;
    const existing = grantBySecretId.get(secret.id);
    markPending(secret.id, true);
    const onSettled = () => markPending(secret.id, false);
    if (existing) {
      revokeMutation.mutate({ grantId: existing.id }, { onSettled });
    } else {
      grantMutation.mutate({ secretId: secret.id, appId }, { onSettled });
    }
  };

  return (
    <div className="flex flex-col gap-3">
      <div className="flex items-center justify-between gap-2 px-1">
        <div className="min-w-0">
          <p className="truncate text-sm font-semibold text-foreground">
            Keys for {appName}
          </p>
          <p className="text-[11px] text-muted-foreground">
            {error
              ? null
              : !canManageVault
                ? "Only workspace owners and admins can manage keys."
                : allApps
                  ? `${typedSecrets.length} available`
                  : `${grantedCount} of ${typedSecrets.length} granted`}
          </p>
        </div>
        {!error && canManageVault ? (
          <Button
            type="button"
            variant="outline"
            size="sm"
            disabled={
              syncMutation.isPending ||
              typedSecrets.length === 0 ||
              (!allApps && grantedCount === 0)
            }
            onClick={() => syncMutation.mutate({ appId })}
            className="h-7 px-2"
          >
            {syncMutation.isPending ? (
              <IconLoader2 className="h-3 w-3 animate-spin" />
            ) : (
              <IconRefresh className="h-3 w-3" />
            )}
            <span className="ml-1 text-xs">Sync</span>
          </Button>
        ) : null}
      </div>

      <div className="max-h-[320px] space-y-1.5 overflow-y-auto rounded-md border border-border bg-card p-1.5">
        {error ? (
          <ActionQueryError
            error={error}
            onRetry={() => {
              void secretsQuery.refetch();
              if (canManageVault) void grantsQuery.refetch();
              void accessQuery.refetch();
            }}
          />
        ) : isLoading ? (
          <div className="space-y-1.5 p-1.5">
            {Array.from({ length: 3 }).map((_, index) => (
              <div
                key={index}
                className="flex items-start gap-3 rounded-md px-2.5 py-2"
              >
                <Skeleton className="mt-0.5 h-4 w-4 shrink-0 rounded" />
                <div className="flex-1 space-y-1.5">
                  <Skeleton className="h-3 w-1/2" />
                  <Skeleton className="h-3 w-3/4" />
                </div>
              </div>
            ))}
          </div>
        ) : typedSecrets.length === 0 ? (
          <p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
            No vault keys yet. Add one from the Vault page.
          </p>
        ) : (
          typedSecrets.map((secret) => {
            const granted = allApps || grantBySecretId.has(secret.id);
            const pending = pendingSecretIds.has(secret.id);
            return (
              <button
                key={secret.id}
                type="button"
                aria-pressed={granted}
                disabled={pending || allApps || !canManageVault}
                onClick={() => toggleSecret(secret)}
                className={`flex w-full items-start gap-3 rounded-md px-2.5 py-2 text-left text-sm disabled:cursor-not-allowed disabled:opacity-60 ${
                  pending || allApps ? "" : "cursor-pointer"
                } ${
                  granted
                    ? "border border-primary/45 bg-primary/5"
                    : "border border-transparent hover:border-muted-foreground/30 hover:bg-accent/35"
                }`}
              >
                <span
                  className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${
                    granted
                      ? "border-primary/60 bg-primary/10 text-primary"
                      : "border-muted-foreground/35 text-transparent"
                  }`}
                >
                  {granted ? <IconCheck className="h-3 w-3" /> : null}
                </span>
                <span className="min-w-0 flex-1">
                  <span className="block truncate font-medium">
                    {secret.credentialKey}
                  </span>
                  <span className="block truncate text-xs text-muted-foreground/70">
                    {allApps
                      ? "Available to this app"
                      : secret.provider || secret.name || "Vault secret"}
                  </span>
                </span>
              </button>
            );
          })
        )}
      </div>
    </div>
  );
}
