"use client";

import type { archestraApiTypes } from "@shared";
import { AlertTriangle, Info, ShieldCheck, User } from "lucide-react";
import { lazy, Suspense, useEffect, useState } from "react";
import {
  StandardDialog,
  StandardFormDialog,
} from "@/components/standard-dialog";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { LinkifiedText } from "@/components/ui/linkified-text";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { useFeature } from "@/lib/config/config.query";
import { useCatalogPresets } from "@/lib/mcp/internal-mcp-catalog.query";
import { useMcpPresetEntries } from "@/lib/mcp/mcp-preset-entry.query";
import { usePresetEntityName } from "@/lib/organization.query";
import { useTeamsWithVaultFolders } from "@/lib/teams/team.query";
import { InstallPresetPicker } from "./install-preset-picker";
import { FillPresetFieldsStep } from "./preset-fallback-fields";
import {
  compileValidationRegex,
  presetHasUnfilledFields,
} from "./preset-helpers";
import {
  type McpServerInstallScope,
  SelectMcpServerCredentialTypeAndTeams,
} from "./select-mcp-server-credential-type-and-teams";

const InlineVaultSecretSelector = lazy(
  // biome-ignore lint/style/noRestrictedImports: lazy loading
  () => import("@/components/inline-vault-secret-selector.ee"),
);

type CatalogItem =
  archestraApiTypes.GetInternalMcpCatalogResponses["200"][number];

type UserConfigType = Record<
  string,
  {
    type: "string" | "number" | "boolean" | "directory" | "file";
    title: string;
    description: string;
    promptOnInstallation?: boolean;
    promptOnPreset?: boolean;
    required?: boolean;
    default?: string | number | boolean | Array<string>;
    multiple?: boolean;
    sensitive?: boolean;
    min?: number;
    max?: number;
  }
>;

export interface RemoteServerInstallResult {
  /** Catalog id to install from — parent or selected preset. */
  catalogId: string;
  metadata: Record<string, unknown>;
  /** Installation scope (personal, team, org) */
  scope: McpServerInstallScope;
  /** Team ID to assign the MCP server to (only when scope is "team") */
  teamId?: string | null;
  /** Whether metadata contains BYOS vault references in path#key format */
  isByosVault?: boolean;
}

interface RemoteServerInstallDialogProps {
  isOpen: boolean;
  onClose: () => void;
  onConfirm: (
    catalogItem: CatalogItem,
    result: RemoteServerInstallResult,
  ) => Promise<void>;
  catalogItem: CatalogItem | null;
  isInstalling: boolean;
  /** When true, shows re-authentication mode (info banner, different title) */
  isReauth?: boolean;
  /** Pre-select a specific team in the credential type selector */
  preselectedTeamId?: string | null;
  /**
   * Pre-select a preset (child catalog) in the InstallPresetPicker when the
   * dialog opens. Falls back to the parent's id when unset.
   */
  preselectedCatalogId?: string | null;
  /** When true, only personal installation is allowed */
  personalOnly?: boolean;
  /** When true, only organization-wide installation is allowed */
  orgOnly?: boolean;
}

export function RemoteServerInstallDialog({
  isOpen,
  onClose,
  onConfirm,
  catalogItem,
  isInstalling,
  isReauth = false,
  preselectedTeamId,
  preselectedCatalogId,
  personalOnly = false,
  orgOnly = false,
}: RemoteServerInstallDialogProps) {
  const [configValues, setConfigValues] = useState<Record<string, string>>({});

  // Team selection state
  const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
  const [scope, setScope] = useState<McpServerInstallScope>(
    orgOnly ? "org" : "personal",
  );
  const [canInstall, setCanInstall] = useState(true);
  const [selectedCatalogId, setSelectedCatalogId] = useState<string>(
    preselectedCatalogId ?? catalogItem?.id ?? "",
  );
  const { data: presets = [] } = useCatalogPresets(catalogItem?.id ?? null);
  const { data: presetEntries = [] } = useMcpPresetEntries();
  const { singular, defaultValidationRegex } = usePresetEntityName();
  const hasPresets = presets.length > 0;

  // Compile the regex once per selected preset. Uses the preset entry's
  // regex for child installs; for the implicit default row (no entry) falls
  // back to the org-wide default validation regex.
  const selectedChild =
    catalogItem && selectedCatalogId && selectedCatalogId !== catalogItem.id
      ? presets.find((p) => p.id === selectedCatalogId)
      : null;
  const presetValidationRegex = (() => {
    const entryId = selectedChild?.presetEntryId ?? null;
    if (!entryId) return compileValidationRegex(defaultValidationRegex);
    const entry = presetEntries.find((e) => e.id === entryId);
    return compileValidationRegex(entry?.validationRegex);
  })();

  // Step 1 ("fill-preset") asks the caller to fill in any preset-scoped fields
  // the selected preset doesn't have values for, persists them onto the preset
  // row, then transitions to Step 2 ("install"). When the preset is already
  // filled (or we're in reauth mode), we start directly at Step 2.
  const selectedPreset =
    selectedCatalogId === catalogItem?.id
      ? catalogItem
      : (presets.find((p) => p.id === selectedCatalogId) ?? null);
  const needsFillStep =
    !isReauth &&
    !!catalogItem &&
    presetHasUnfilledFields(catalogItem, selectedPreset);
  const [step, setStep] = useState<"fill-preset" | "install">(
    needsFillStep ? "fill-preset" : "install",
  );

  useEffect(() => {
    if (isOpen && catalogItem) {
      setSelectedCatalogId(preselectedCatalogId ?? catalogItem.id);
    }
  }, [isOpen, catalogItem, preselectedCatalogId]);

  useEffect(() => {
    if (!isOpen) return;
    setStep(needsFillStep ? "fill-preset" : "install");
  }, [isOpen, needsFillStep]);

  // Vault team selection (separate from install team for personal + BYOS)
  const [vaultTeamId, setVaultTeamId] = useState<string | null>(null);

  // BYOS (Bring Your Own Secrets) state - per-field vault references
  const [vaultSecrets, setVaultSecrets] = useState<
    Record<string, { path: string | null; key: string | null }>
  >({});

  const byosEnabled = useFeature("byosEnabled");
  const { data: teamsWithVault } = useTeamsWithVaultFolders();
  const vaultTeams = teamsWithVault?.filter((t) => t.vaultPath);
  const userConfig =
    (catalogItem?.userConfig as UserConfigType | null | undefined) || {};
  const hasPromptSensitiveFields = Object.values(userConfig).some(
    (config) =>
      config.sensitive &&
      config.promptOnInstallation !== false &&
      !config.promptOnPreset,
  );

  // Helper to update vault secret for a specific field
  const updateVaultSecret = (
    fieldName: string,
    prop: "path" | "key",
    value: string | null,
  ) => {
    setVaultSecrets((prev) => ({
      ...prev,
      [fieldName]: {
        ...prev[fieldName],
        [prop]: value,
        // Reset key when path changes
        ...(prop === "path" ? { key: null } : {}),
      },
    }));
  };

  // Sync vaultTeamId from selectedTeamId when in team mode, reset otherwise
  useEffect(() => {
    if (scope === "team") {
      setVaultTeamId(selectedTeamId);
    } else {
      setVaultTeamId(null);
    }
    setVaultSecrets({});
  }, [scope, selectedTeamId]);

  const handleVaultTeamChange = (teamId: string) => {
    setVaultTeamId(teamId);
    setVaultSecrets({});
  };

  // Show vault selector only when BYOS is enabled and sensitive fields exist.
  const useVaultSecrets = byosEnabled && hasPromptSensitiveFields;

  const handleConfirm = async () => {
    if (!catalogItem || step !== "install") {
      return;
    }

    try {
      const metadata: Record<string, unknown> = {};

      for (const [fieldName, fieldConfig] of Object.entries(userConfig)) {
        if (
          fieldConfig.promptOnInstallation === false ||
          fieldConfig.promptOnPreset
        ) {
          continue;
        }

        // For BYOS mode, sensitive fields use vault references
        if (useVaultSecrets && fieldConfig.sensitive) {
          const vaultRef = vaultSecrets[fieldName];
          if (vaultRef?.path && vaultRef?.key) {
            // Store as path#key format for BYOS vault resolution
            metadata[fieldName] = `${vaultRef.path}#${vaultRef.key}`;
          }
        } else {
          // Non-sensitive fields or non-BYOS mode: use manual value
          const value = configValues[fieldName];
          if (value !== undefined && value !== "") {
            switch (fieldConfig.type) {
              case "number":
                metadata[fieldName] = Number(value);
                break;
              case "boolean":
                metadata[fieldName] = value === "true";
                break;
              default:
                metadata[fieldName] = value;
            }
          }
        }
      }

      await onConfirm(catalogItem, {
        catalogId: selectedCatalogId || catalogItem.id,
        metadata,
        scope,
        teamId: selectedTeamId,
        isByosVault: useVaultSecrets,
      });
      resetForm();
      onClose();
    } catch (_error) {
      // Error handling is done in the parent component
    }
  };

  const resetForm = () => {
    setConfigValues({});
    setSelectedTeamId(null);
    setScope(orgOnly ? "org" : "personal");
    setVaultTeamId(null);
    setVaultSecrets({});
  };

  const handleClose = () => {
    resetForm();
    onClose();
  };

  if (!catalogItem) {
    return null;
  }

  const promptableUserConfig = Object.fromEntries(
    Object.entries(userConfig).filter(([_fieldName, fieldConfig]) => {
      return (
        fieldConfig.promptOnInstallation !== false &&
        !fieldConfig.promptOnPreset
      );
    }),
  );
  const hasConfig = Object.keys(promptableUserConfig).length > 0;
  const hasOAuth = !!catalogItem.oauthConfig;
  const usesBrowserOAuth =
    catalogItem.oauthConfig?.grant_type !== "client_credentials";

  // Get sensitive and non-sensitive required fields
  const sensitiveRequiredFields = Object.entries(promptableUserConfig).filter(
    ([_, cfg]) => cfg.required && cfg.sensitive,
  );
  const nonSensitiveRequiredFields = Object.entries(
    promptableUserConfig,
  ).filter(([_, cfg]) => cfg.required && !cfg.sensitive);

  // Check if non-sensitive required fields are valid (always need manual input)
  const isNonSensitiveValid = nonSensitiveRequiredFields.every(([fieldName]) =>
    configValues[fieldName]?.trim(),
  );

  // Check if sensitive required fields are valid:
  // - BYOS mode: vault path AND key must be selected for each
  // - Normal mode: manual values must be filled
  const isSensitiveValid = useVaultSecrets
    ? sensitiveRequiredFields.every(
        ([fieldName]) =>
          vaultSecrets[fieldName]?.path && vaultSecrets[fieldName]?.key,
      )
    : sensitiveRequiredFields.every(([fieldName]) =>
        configValues[fieldName]?.trim(),
      );

  // Run the preset regex against every prompted-string user field. Skips
  // numbers, booleans, empty values, and BYOS vault-backed sensitive fields
  // (whose value isn't a free-text string).
  const fieldRegexErrors: Record<string, string | null> = {};
  if (presetValidationRegex) {
    for (const [fieldName, fieldConfig] of Object.entries(
      promptableUserConfig,
    )) {
      if (
        fieldConfig.type !== "string" &&
        fieldConfig.type !== "directory" &&
        fieldConfig.type !== "file"
      ) {
        continue;
      }
      if (useVaultSecrets && fieldConfig.sensitive) continue;
      const v = configValues[fieldName] ?? "";
      if (!v) continue;
      fieldRegexErrors[fieldName] = presetValidationRegex.test(v)
        ? null
        : `Value does not match the ${singular} Validation Rule`;
    }
  }
  const hasRegexErrors = Object.values(fieldRegexErrors).some((e) => !!e);

  const isValid =
    (!hasConfig || (isNonSensitiveValid && isSensitiveValid)) &&
    !hasRegexErrors;

  if (step === "fill-preset") {
    return (
      <StandardDialog
        open={isOpen}
        onOpenChange={handleClose}
        title={
          <div className="flex items-end gap-2">
            <User className="h-5 w-5" />
            <span>
              Install Server
              <span className="text-muted-foreground ml-2 font-normal">
                {catalogItem.name}
              </span>
            </span>
          </div>
        }
        size="medium"
        bodyClassName="grid gap-6"
      >
        <FillPresetFieldsStep
          catalog={catalogItem}
          selectedPresetId={selectedCatalogId || catalogItem.id}
          onSaved={() => setStep("install")}
          onCancel={handleClose}
        />
      </StandardDialog>
    );
  }

  return (
    <StandardFormDialog
      open={isOpen}
      onOpenChange={handleClose}
      title={
        <div className="flex items-center justify-between gap-2">
          <div className="flex items-end gap-2">
            <User className="h-5 w-5" />
            <span>
              {isReauth ? "Re-authenticate" : "Install Server"}
              <span className="text-muted-foreground ml-2 font-normal">
                {catalogItem.name}
              </span>
            </span>
          </div>
          {hasOAuth ? (
            <Badge variant="secondary" className="flex items-center gap-1">
              <ShieldCheck className="h-3 w-3" />
              OAuth
            </Badge>
          ) : null}
        </div>
      }
      size="medium"
      bodyClassName="grid gap-6"
      onSubmit={handleConfirm}
      footer={
        canInstall ? (
          <>
            <Button
              type="button"
              variant="outline"
              onClick={handleClose}
              disabled={isInstalling}
            >
              Cancel
            </Button>
            <Button type="submit" disabled={!isValid || isInstalling}>
              {isInstalling
                ? isReauth
                  ? "Updating..."
                  : "Installing..."
                : isReauth
                  ? "Update Credentials"
                  : "Install"}
            </Button>
          </>
        ) : null
      }
    >
      {isReauth && (
        <Alert className="border-amber-500/50 bg-amber-500/10">
          <AlertTriangle className="h-4 w-4 text-amber-500" />
          <AlertDescription>
            Your existing credentials are expired or invalid. Submitting new
            credentials here will replace them while preserving your tool
            assignments.
          </AlertDescription>
        </Alert>
      )}

      <SelectMcpServerCredentialTypeAndTeams
        onTeamChange={setSelectedTeamId}
        catalogId={selectedCatalogId || catalogItem?.id}
        onScopeChange={setScope}
        onCanInstallChange={setCanInstall}
        preselectedTeamId={preselectedTeamId}
        personalOnly={personalOnly}
        orgOnly={orgOnly}
        hasPresets={hasPresets && !isReauth}
        presetPicker={
          !isReauth && catalogItem && hasPresets ? (
            <InstallPresetPicker
              parent={catalogItem}
              value={selectedCatalogId}
              onChange={setSelectedCatalogId}
            />
          ) : null
        }
      />

      {useVaultSecrets && scope !== "team" && (
        <div className="space-y-2">
          <Label>Pull Vault secrets from:</Label>
          <p className="text-xs text-muted-foreground">
            Only folders associated with your teams are shown.
          </p>
          <Select
            value={vaultTeamId ?? ""}
            onValueChange={handleVaultTeamChange}
          >
            <SelectTrigger>
              <SelectValue placeholder="-- Select Vault folder --" />
            </SelectTrigger>
            <SelectContent>
              {vaultTeams?.map((team) => (
                <SelectItem key={team.id} value={team.id}>
                  {team.vaultPath}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
      )}

      {canInstall && hasOAuth && usesBrowserOAuth && (
        <Alert>
          <Info className="h-4 w-4" />
          <AlertDescription>
            This server requires OAuth authentication. You'll be redirected to
            complete the authentication flow after clicking Install.
          </AlertDescription>
        </Alert>
      )}

      {canInstall && hasOAuth && !usesBrowserOAuth && (
        <Alert>
          <Info className="h-4 w-4" />
          <AlertDescription>
            This server uses shared OAuth client credentials. The values below
            are stored with the installation, and {catalogItem.name} will fetch
            short-lived bearer tokens automatically when tools run.
          </AlertDescription>
        </Alert>
      )}

      {/* Config fields - always show when config exists */}
      {canInstall && hasConfig && (
        <div className="space-y-4">
          {Object.entries(promptableUserConfig).map(
            ([fieldName, fieldConfig]) => (
              <div key={fieldName} className="grid gap-2">
                {fieldConfig.type === "boolean" ? (
                  <div className="flex items-center gap-2">
                    <Checkbox
                      id={fieldName}
                      checked={configValues[fieldName] === "true"}
                      onCheckedChange={(checked) =>
                        setConfigValues((prev) => ({
                          ...prev,
                          [fieldName]: checked ? "true" : "false",
                        }))
                      }
                    />
                    <Label htmlFor={fieldName} className="cursor-pointer">
                      {fieldConfig.title}
                      {fieldConfig.required && (
                        <span className="text-red-500"> *</span>
                      )}
                    </Label>
                  </div>
                ) : (
                  <Label htmlFor={fieldName}>
                    {fieldConfig.title}
                    {fieldConfig.required && (
                      <span className="text-red-500"> *</span>
                    )}
                  </Label>
                )}
                {fieldConfig.description && (
                  <p className="text-xs text-muted-foreground">
                    <LinkifiedText>{fieldConfig.description}</LinkifiedText>
                  </p>
                )}

                {/* BYOS mode: vault selector for sensitive fields */}
                {fieldConfig.type ===
                "boolean" ? null : fieldConfig.sensitive && useVaultSecrets ? (
                  <Suspense
                    fallback={
                      <div className="text-sm text-muted-foreground">
                        Loading...
                      </div>
                    }
                  >
                    <InlineVaultSecretSelector
                      teamId={vaultTeamId}
                      selectedSecretPath={vaultSecrets[fieldName]?.path ?? null}
                      selectedSecretKey={vaultSecrets[fieldName]?.key ?? null}
                      onSecretPathChange={(path) =>
                        updateVaultSecret(fieldName, "path", path)
                      }
                      onSecretKeyChange={(key) =>
                        updateVaultSecret(fieldName, "key", key)
                      }
                      disabled={isInstalling}
                      noTeamMessage={
                        scope !== "team"
                          ? "Select a vault folder to pull secrets from"
                          : undefined
                      }
                    />
                  </Suspense>
                ) : (
                  <>
                    <Input
                      id={fieldName}
                      type={
                        fieldConfig.sensitive
                          ? "password"
                          : fieldConfig.type === "number"
                            ? "number"
                            : "text"
                      }
                      placeholder={
                        fieldConfig.default?.toString() ||
                        fieldConfig.description
                      }
                      value={configValues[fieldName] || ""}
                      onChange={(e) =>
                        setConfigValues((prev) => ({
                          ...prev,
                          [fieldName]: e.target.value,
                        }))
                      }
                      min={fieldConfig.min}
                      max={fieldConfig.max}
                      aria-invalid={
                        fieldRegexErrors[fieldName] ? true : undefined
                      }
                    />
                    {fieldRegexErrors[fieldName] && (
                      <p className="text-xs text-destructive">
                        {fieldRegexErrors[fieldName]}
                      </p>
                    )}
                  </>
                )}
              </div>
            ),
          )}
        </div>
      )}

      {canInstall && catalogItem.serverUrl && (
        <div className="rounded-md bg-muted p-4">
          <h4 className="text-sm font-medium mb-2">Server Details:</h4>
          <div className="space-y-1">
            <p className="text-sm text-muted-foreground">
              <span className="font-medium">URL:</span> {catalogItem.serverUrl}
            </p>
            {catalogItem.docsUrl && (
              <p className="text-sm text-muted-foreground">
                <span className="font-medium">Documentation:</span>{" "}
                <a
                  href={catalogItem.docsUrl}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="text-primary hover:underline"
                >
                  {catalogItem.docsUrl}
                </a>
              </p>
            )}
          </div>
        </div>
      )}
    </StandardFormDialog>
  );
}
