"use client";

import { useEffect, useMemo, useState } from "react";
import { Check, Loader2 } from "lucide-react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  addOnboardingProvider,
  completeOnboarding,
  configureOidcProvider,
  createBreakglassAdmin,
  fetchOnboardingStatus,
  testOnboardingProvider,
} from "@/lib/api/onboarding";

type WizardStep = 1 | 2 | 3 | 4;
type ProviderType = "openai" | "anthropic" | "zai" | "ollama" | "custom";

interface StepDefinition {
  id: WizardStep;
  label: string;
}

interface ProviderOption {
  value: ProviderType;
  label: string;
}

const STEPS: StepDefinition[] = [
  { id: 1, label: "1. Admin" },
  { id: 2, label: "2. Auth" },
  { id: 3, label: "3. Provider" },
  { id: 4, label: "4. Launch" },
];

const PROVIDER_OPTIONS: ProviderOption[] = [
  { value: "openai", label: "OpenAI" },
  { value: "anthropic", label: "Anthropic" },
  { value: "zai", label: "Z.ai" },
  { value: "ollama", label: "Ollama" },
  { value: "custom", label: "Custom" },
];

const CLOUD_PROVIDER_TYPES = new Set<ProviderType>(["openai", "anthropic", "zai"]);
const BASE_URL_PROVIDER_TYPES = new Set<ProviderType>(["ollama", "custom"]);

function getErrorMessage(error: unknown, fallback: string): string {
  if (error instanceof Error && error.message.trim().length > 0) {
    return error.message;
  }

  return fallback;
}

function isValidHttpUrl(value: string): boolean {
  try {
    const parsed = new URL(value);
    return parsed.protocol === "http:" || parsed.protocol === "https:";
  } catch {
    return false;
  }
}

function mapProviderTypeToApi(type: ProviderType): string {
  switch (type) {
    case "anthropic":
      return "claude";
    case "zai":
      return "openai";
    case "custom":
      return "openai";
    default:
      return type;
  }
}

function getProviderDefaultBaseUrl(type: ProviderType): string | undefined {
  switch (type) {
    case "ollama":
      return "http://localhost:11434";
    case "anthropic":
      return "https://api.anthropic.com/v1";
    case "zai":
      return "https://api.z.ai/v1";
    default:
      return undefined;
  }
}

function buildProviderName(displayName: string, type: ProviderType): string {
  const slug = displayName
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+/, "")
    .replace(/-+$/, "");

  if (slug.length > 0) {
    return slug;
  }

  return `${type}-provider`;
}

function constantTimeEquals(left: string, right: string): boolean {
  if (left.length !== right.length) {
    return false;
  }

  let mismatch = 0;
  for (let index = 0; index < left.length; index += 1) {
    mismatch |= left.charCodeAt(index) ^ right.charCodeAt(index);
  }

  return mismatch === 0;
}

export function OnboardingWizard(): React.JSX.Element {
  const router = useRouter();

  const [currentStep, setCurrentStep] = useState<WizardStep>(1);
  const [isCheckingStatus, setIsCheckingStatus] = useState(true);

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [isCreatingAdmin, setIsCreatingAdmin] = useState(false);
  const [configuredUsername, setConfiguredUsername] = useState<string | null>(null);

  const [issuerUrl, setIssuerUrl] = useState("");
  const [clientId, setClientId] = useState("");
  const [clientSecret, setClientSecret] = useState("");
  const [isConfiguringOidc, setIsConfiguringOidc] = useState(false);
  const [oidcConfigured, setOidcConfigured] = useState(false);

  const [providerType, setProviderType] = useState<ProviderType>("openai");
  const [displayName, setDisplayName] = useState("");
  const [providerApiKey, setProviderApiKey] = useState("");
  const [providerBaseUrl, setProviderBaseUrl] = useState("");
  const [isTestingProvider, setIsTestingProvider] = useState(false);
  const [isAddingProvider, setIsAddingProvider] = useState(false);
  const [providerConfigured, setProviderConfigured] = useState<{
    displayName: string;
    type: ProviderType;
  } | null>(null);
  const [providerTestMessage, setProviderTestMessage] = useState<string | null>(null);
  const [providerTestSucceeded, setProviderTestSucceeded] = useState(false);
  const [testedProviderSignature, setTestedProviderSignature] = useState<string | null>(null);

  const [isCompleting, setIsCompleting] = useState(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  const requiresApiKey = CLOUD_PROVIDER_TYPES.has(providerType);
  const requiresBaseUrl = BASE_URL_PROVIDER_TYPES.has(providerType);
  const apiProviderType = mapProviderTypeToApi(providerType);
  const resolvedProviderBaseUrl =
    requiresBaseUrl && providerBaseUrl.trim().length > 0
      ? providerBaseUrl.trim()
      : getProviderDefaultBaseUrl(providerType);

  const providerTestPayload = useMemo(() => {
    const payload: { type: string; baseUrl?: string; apiKey?: string } = {
      type: apiProviderType,
    };

    if (resolvedProviderBaseUrl !== undefined && resolvedProviderBaseUrl.length > 0) {
      payload.baseUrl = resolvedProviderBaseUrl;
    }

    const trimmedApiKey = providerApiKey.trim();
    if (requiresApiKey && trimmedApiKey.length > 0) {
      payload.apiKey = trimmedApiKey;
    }

    return payload;
  }, [apiProviderType, providerApiKey, requiresApiKey, resolvedProviderBaseUrl]);

  const providerPayloadSignature = useMemo(
    () => JSON.stringify(providerTestPayload),
    [providerTestPayload]
  );

  const canAddProvider =
    providerTestSucceeded &&
    testedProviderSignature === providerPayloadSignature &&
    !isTestingProvider &&
    !isAddingProvider;

  useEffect(() => {
    let cancelled = false;

    async function loadStatus(): Promise<void> {
      try {
        const status = await fetchOnboardingStatus();
        if (!cancelled && status.completed) {
          router.push("/");
          return;
        }
      } catch {
        // Status check failure should not block setup UI.
      } finally {
        if (!cancelled) {
          setIsCheckingStatus(false);
        }
      }
    }

    void loadStatus();

    return (): void => {
      cancelled = true;
    };
  }, [router]);

  const resetProviderVerification = (): void => {
    setProviderTestSucceeded(false);
    setTestedProviderSignature(null);
    setProviderTestMessage(null);
  };

  const validateAdminStep = (): boolean => {
    if (username.trim().length < 3) {
      setErrorMessage("Username must be at least 3 characters.");
      return false;
    }

    if (password.length < 8) {
      setErrorMessage("Password must be at least 8 characters.");
      return false;
    }

    if (!constantTimeEquals(password, confirmPassword)) {
      setErrorMessage("Passwords do not match.");
      return false;
    }

    return true;
  };

  const validateOidcStep = (): boolean => {
    if (issuerUrl.trim().length === 0 || !isValidHttpUrl(issuerUrl.trim())) {
      setErrorMessage("Issuer URL must be a valid URL.");
      return false;
    }

    if (clientId.trim().length === 0) {
      setErrorMessage("Client ID is required.");
      return false;
    }

    if (clientSecret.trim().length === 0) {
      setErrorMessage("Client secret is required.");
      return false;
    }

    return true;
  };

  const validateProviderStep = (): boolean => {
    if (displayName.trim().length === 0) {
      setErrorMessage("Display name is required.");
      return false;
    }

    if (requiresApiKey && providerApiKey.trim().length === 0) {
      setErrorMessage("API key is required for this provider.");
      return false;
    }

    if (requiresBaseUrl && providerBaseUrl.trim().length === 0) {
      setErrorMessage("Base URL is required for this provider.");
      return false;
    }

    if (requiresBaseUrl && !isValidHttpUrl(providerBaseUrl.trim())) {
      setErrorMessage("Base URL must be a valid URL.");
      return false;
    }

    return true;
  };

  const handleCreateAdmin = async (event: React.SyntheticEvent<HTMLFormElement>): Promise<void> => {
    event.preventDefault();

    setErrorMessage(null);
    if (!validateAdminStep()) {
      return;
    }

    setIsCreatingAdmin(true);
    try {
      const result = await createBreakglassAdmin({
        username: username.trim(),
        password,
      });
      setConfiguredUsername(result.username);
      setCurrentStep(2);
    } catch (error) {
      setErrorMessage(getErrorMessage(error, "Failed to create admin account."));
    } finally {
      setIsCreatingAdmin(false);
    }
  };

  const handleConfigureOidc = async (
    event: React.SyntheticEvent<HTMLFormElement>
  ): Promise<void> => {
    event.preventDefault();

    setErrorMessage(null);
    if (!validateOidcStep()) {
      return;
    }

    setIsConfiguringOidc(true);
    try {
      await configureOidcProvider({
        issuerUrl: issuerUrl.trim(),
        clientId: clientId.trim(),
        clientSecret: clientSecret.trim(),
      });
      setOidcConfigured(true);
      setCurrentStep(3);
    } catch (error) {
      setErrorMessage(getErrorMessage(error, "Failed to configure OIDC provider."));
    } finally {
      setIsConfiguringOidc(false);
    }
  };

  const handleSkipOidc = (): void => {
    setErrorMessage(null);
    setOidcConfigured(false);
    setCurrentStep(3);
  };

  const handleTestProvider = async (): Promise<void> => {
    setErrorMessage(null);
    setProviderTestMessage(null);
    if (!validateProviderStep()) {
      return;
    }

    setIsTestingProvider(true);
    try {
      const response = await testOnboardingProvider(providerTestPayload);
      if (!response.success) {
        setProviderTestSucceeded(false);
        setTestedProviderSignature(null);
        setErrorMessage(response.error ?? "Connection test failed.");
        return;
      }

      setProviderTestSucceeded(true);
      setTestedProviderSignature(providerPayloadSignature);
      setProviderTestMessage("Connection successful.");
    } catch (error) {
      setProviderTestSucceeded(false);
      setTestedProviderSignature(null);
      setErrorMessage(getErrorMessage(error, "Connection test failed."));
    } finally {
      setIsTestingProvider(false);
    }
  };

  const handleAddProvider = async (): Promise<void> => {
    setErrorMessage(null);
    if (!validateProviderStep()) {
      return;
    }

    if (!canAddProvider) {
      setErrorMessage("Test connection successfully before adding the provider.");
      return;
    }

    setIsAddingProvider(true);
    try {
      const trimmedDisplayName = displayName.trim();
      const payload: {
        name: string;
        displayName: string;
        type: string;
        baseUrl?: string;
        apiKey?: string;
      } = {
        name: buildProviderName(trimmedDisplayName, providerType),
        displayName: trimmedDisplayName,
        type: apiProviderType,
      };

      if (resolvedProviderBaseUrl !== undefined && resolvedProviderBaseUrl.length > 0) {
        payload.baseUrl = resolvedProviderBaseUrl;
      }

      const trimmedApiKey = providerApiKey.trim();
      if (requiresApiKey && trimmedApiKey.length > 0) {
        payload.apiKey = trimmedApiKey;
      }

      await addOnboardingProvider(payload);

      setProviderConfigured({ displayName: trimmedDisplayName, type: providerType });
      setCurrentStep(4);
    } catch (error) {
      setErrorMessage(getErrorMessage(error, "Failed to add provider."));
    } finally {
      setIsAddingProvider(false);
    }
  };

  const handleCompleteOnboarding = async (): Promise<void> => {
    setErrorMessage(null);
    setIsCompleting(true);
    try {
      await completeOnboarding();
      router.push("/");
    } catch (error) {
      setErrorMessage(getErrorMessage(error, "Failed to complete onboarding."));
    } finally {
      setIsCompleting(false);
    }
  };

  const providerLabel =
    PROVIDER_OPTIONS.find((option) => option.value === providerConfigured?.type)?.label ??
    providerConfigured?.type ??
    "Unknown";

  return (
    <Card className="mx-auto w-full max-w-2xl shadow-sm">
      <CardHeader>
        <CardTitle>First-boot onboarding</CardTitle>
        <CardDescription>Set up your admin access, auth, and first provider.</CardDescription>
      </CardHeader>
      <CardContent className="space-y-6">
        <div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
          {STEPS.map((step) => {
            const isActive = currentStep === step.id;
            const isComplete = currentStep > step.id;
            const badgeClass = isComplete
              ? "bg-emerald-100 text-emerald-700 border-emerald-200"
              : isActive
                ? "bg-blue-100 text-blue-700 border-blue-200"
                : "bg-gray-100 text-gray-500 border-gray-200";

            return (
              <div
                key={step.id}
                className={`rounded-md border px-3 py-2 text-sm ${badgeClass}`}
                aria-current={isActive ? "step" : undefined}
              >
                <div className="flex items-center gap-2 font-medium">
                  <span className="inline-flex h-5 w-5 items-center justify-center rounded-full border border-current text-xs">
                    {isComplete ? <Check className="h-3.5 w-3.5" aria-hidden="true" /> : step.id}
                  </span>
                  <span>{step.label}</span>
                </div>
              </div>
            );
          })}
        </div>

        {isCheckingStatus ? (
          <div className="flex items-center gap-2 text-sm text-gray-600">
            <Loader2 className="h-4 w-4 animate-spin" aria-hidden="true" />
            <span>Checking onboarding status...</span>
          </div>
        ) : (
          <>
            {currentStep === 1 && (
              <form onSubmit={handleCreateAdmin} className="space-y-4" noValidate>
                <div className="space-y-1">
                  <h2 className="text-xl font-semibold">
                    Welcome to Mosaic Stack. Let's get you set up.
                  </h2>
                  <p className="text-sm text-gray-600">
                    Create a breakglass admin account for emergency access.
                  </p>
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-username">Username</Label>
                  <Input
                    id="onboarding-username"
                    value={username}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setUsername(event.target.value);
                    }}
                    disabled={isCreatingAdmin}
                    autoComplete="username"
                  />
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-password">Password</Label>
                  <Input
                    id="onboarding-password"
                    type="password"
                    value={password}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setPassword(event.target.value);
                    }}
                    disabled={isCreatingAdmin}
                    autoComplete="new-password"
                  />
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-confirm-password">Confirm Password</Label>
                  <Input
                    id="onboarding-confirm-password"
                    type="password"
                    value={confirmPassword}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setConfirmPassword(event.target.value);
                    }}
                    disabled={isCreatingAdmin}
                    autoComplete="new-password"
                  />
                </div>

                <Button type="submit" disabled={isCreatingAdmin}>
                  {isCreatingAdmin && (
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
                  )}
                  <span>Create Admin</span>
                </Button>
              </form>
            )}

            {currentStep === 2 && (
              <form onSubmit={handleConfigureOidc} className="space-y-4" noValidate>
                <div className="space-y-1">
                  <h2 className="text-xl font-semibold">Configure OIDC Provider (Optional)</h2>
                  <p className="text-sm text-gray-600">
                    You can skip this for now and continue with breakglass-only authentication.
                  </p>
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-issuer-url">OIDC Issuer URL</Label>
                  <Input
                    id="onboarding-issuer-url"
                    value={issuerUrl}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setIssuerUrl(event.target.value);
                    }}
                    disabled={isConfiguringOidc}
                    placeholder="https://auth.example.com/application/o/mosaic/"
                    autoComplete="url"
                  />
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-client-id">Client ID</Label>
                  <Input
                    id="onboarding-client-id"
                    value={clientId}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setClientId(event.target.value);
                    }}
                    disabled={isConfiguringOidc}
                  />
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-client-secret">Client Secret</Label>
                  <Input
                    id="onboarding-client-secret"
                    type="password"
                    value={clientSecret}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setClientSecret(event.target.value);
                    }}
                    disabled={isConfiguringOidc}
                    autoComplete="off"
                  />
                </div>

                <div className="flex flex-wrap gap-2">
                  <Button type="submit" disabled={isConfiguringOidc}>
                    {isConfiguringOidc && (
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
                    )}
                    <span>Configure OIDC</span>
                  </Button>
                  <Button
                    type="button"
                    variant="outline"
                    onClick={handleSkipOidc}
                    disabled={isConfiguringOidc}
                  >
                    Skip
                  </Button>
                </div>
              </form>
            )}

            {currentStep === 3 && (
              <div className="space-y-4">
                <div className="space-y-1">
                  <h2 className="text-xl font-semibold">Add Your First LLM Provider</h2>
                  <p className="text-sm text-gray-600">
                    Test the connection before adding your provider.
                  </p>
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-provider-type">Provider Type</Label>
                  <Select
                    value={providerType}
                    onValueChange={(value) => {
                      const nextType = value as ProviderType;
                      setProviderType(nextType);
                      setProviderApiKey("");
                      setProviderBaseUrl(
                        BASE_URL_PROVIDER_TYPES.has(nextType)
                          ? (getProviderDefaultBaseUrl(nextType) ?? "")
                          : ""
                      );
                      resetProviderVerification();
                      setErrorMessage(null);
                    }}
                    disabled={isTestingProvider || isAddingProvider}
                  >
                    <SelectTrigger id="onboarding-provider-type">
                      <SelectValue placeholder="Select provider type" />
                    </SelectTrigger>
                    <SelectContent>
                      {PROVIDER_OPTIONS.map((option) => (
                        <SelectItem key={option.value} value={option.value}>
                          {option.label}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>

                <div className="grid gap-2">
                  <Label htmlFor="onboarding-provider-display-name">Display Name</Label>
                  <Input
                    id="onboarding-provider-display-name"
                    value={displayName}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      setDisplayName(event.target.value);
                      resetProviderVerification();
                      setErrorMessage(null);
                    }}
                    disabled={isTestingProvider || isAddingProvider}
                    placeholder="My OpenAI Provider"
                  />
                </div>

                {requiresApiKey && (
                  <div className="grid gap-2">
                    <Label htmlFor="onboarding-provider-api-key">API Key</Label>
                    <Input
                      id="onboarding-provider-api-key"
                      type="password"
                      value={providerApiKey}
                      onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                        setProviderApiKey(event.target.value);
                        resetProviderVerification();
                        setErrorMessage(null);
                      }}
                      disabled={isTestingProvider || isAddingProvider}
                      autoComplete="off"
                    />
                  </div>
                )}

                {requiresBaseUrl && (
                  <div className="grid gap-2">
                    <Label htmlFor="onboarding-provider-base-url">Base URL</Label>
                    <Input
                      id="onboarding-provider-base-url"
                      value={providerBaseUrl}
                      onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                        setProviderBaseUrl(event.target.value);
                        resetProviderVerification();
                        setErrorMessage(null);
                      }}
                      disabled={isTestingProvider || isAddingProvider}
                      placeholder="http://localhost:11434"
                      autoComplete="url"
                    />
                  </div>
                )}

                {providerTestMessage && (
                  <p className="text-sm text-emerald-700" role="status">
                    {providerTestMessage}
                  </p>
                )}

                <div className="flex flex-wrap gap-2">
                  <Button
                    type="button"
                    variant="outline"
                    onClick={() => {
                      void handleTestProvider();
                    }}
                    disabled={isTestingProvider || isAddingProvider}
                  >
                    {isTestingProvider && (
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
                    )}
                    <span>Test Connection</span>
                  </Button>
                  <Button
                    type="button"
                    onClick={() => {
                      void handleAddProvider();
                    }}
                    disabled={!canAddProvider}
                  >
                    {isAddingProvider && (
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
                    )}
                    <span>Add Provider</span>
                  </Button>
                </div>
              </div>
            )}

            {currentStep === 4 && (
              <div className="space-y-4">
                <div className="space-y-1">
                  <h2 className="text-xl font-semibold">You're all set</h2>
                  <p className="text-sm text-gray-600">
                    Review the setup summary and launch Mosaic Stack.
                  </p>
                </div>

                <div className="rounded-md border bg-gray-50 p-4">
                  <ul className="space-y-2 text-sm">
                    <li>
                      <span className="font-medium">Admin:</span>{" "}
                      {configuredUsername ? `${configuredUsername} configured` : "Not configured"}
                    </li>
                    <li>
                      <span className="font-medium">OIDC:</span>{" "}
                      {oidcConfigured ? "Configured" : "Skipped for now"}
                    </li>
                    <li>
                      <span className="font-medium">LLM Provider:</span>{" "}
                      {providerConfigured
                        ? `${providerConfigured.displayName} (${providerLabel})`
                        : "Not configured"}
                    </li>
                  </ul>
                </div>

                <Button
                  type="button"
                  onClick={() => void handleCompleteOnboarding()}
                  disabled={isCompleting}
                >
                  {isCompleting && (
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
                  )}
                  <span>Launch Mosaic Stack</span>
                </Button>
              </div>
            )}
          </>
        )}

        {errorMessage && (
          <p className="text-sm text-red-600" role="alert">
            {errorMessage}
          </p>
        )}
      </CardContent>
    </Card>
  );
}

export default OnboardingWizard;
