import { Button } from "@/components/ui/button";
import { EnvVarInput } from "@/components/ui/envVarInput";
import { HeadersTable } from "@/components/ui/headersTable";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Switch } from "@/components/ui/switch";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useToast } from "@/hooks/use-toast";
import { getErrorMessage, useCreateMCPClientMutation } from "@/lib/store";
import { CreateMCPClientRequest, EnvVar, MCPAuthType, MCPConnectionType, MCPStdioConfig, OAuthConfig } from "@/lib/types/mcp";
import { parseArrayFromText } from "@/lib/utils/array";
import { Validator } from "@/lib/utils/validation";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import { Info } from "lucide-react";
import React, { useEffect, useState } from "react";
import { OAuth2Authorizer } from "./oauth2Authorizer";

interface ClientFormProps {
	open: boolean;
	onClose: () => void;
	onSaved: () => void;
}

const emptyStdioConfig: MCPStdioConfig = {
	command: "",
	args: [],
	envs: [],
};

const emptyEnvVar: EnvVar = { value: "", env_var: "", from_env: false };

const emptyOAuthConfig: OAuthConfig = {
	client_id: "",
	client_secret: "",
	authorize_url: "",
	token_url: "",
	scopes: [],
};

const emptyForm: CreateMCPClientRequest = {
	name: "",
	is_code_mode_client: false,
	is_ping_available: true,
	connection_type: "http",
	connection_string: emptyEnvVar,
	stdio_config: emptyStdioConfig,
	auth_type: "none",
};

const ClientForm: React.FC<ClientFormProps> = ({ open, onClose, onSaved }) => {
	const hasCreateMCPClientAccess = useRbac(RbacResource.MCPGateway, RbacOperation.Create);
	const [form, setForm] = useState<CreateMCPClientRequest>(emptyForm);
	const [isLoading, setIsLoading] = useState(false);
	const [argsText, setArgsText] = useState("");
	const [envsText, setEnvsText] = useState("");
	const [scopesText, setScopesText] = useState("");
	const [oauthFlow, setOauthFlow] = useState<{
		authorizeUrl: string;
		oauthConfigId: string;
		mcpClientId: string;
		isPerUserOauth?: boolean;
	} | null>(null);
	const { toast } = useToast();

	// RTK Query mutations
	const [createMCPClient] = useCreateMCPClientMutation();

	// Reset form state when dialog opens
	useEffect(() => {
		if (open) {
			setForm(emptyForm);
			setArgsText("");
			setEnvsText("");
			setScopesText("");
			setOauthFlow(null);
			setIsLoading(false);
		}
	}, [open]);

	const handleChange = (
		field: keyof CreateMCPClientRequest,
		value: string | string[] | boolean | MCPConnectionType | MCPStdioConfig | undefined,
	) => {
		setForm((prev) => {
			if (field === "connection_type" && value === "stdio") {
				return {
					...prev,
					connection_type: "stdio" as MCPConnectionType,
					auth_type: "none" as MCPAuthType,
					headers: undefined,
					oauth_config: undefined,
				};
			}
			return { ...prev, [field]: value };
		});
	};

	const handleStdioConfigChange = (field: keyof MCPStdioConfig, value: string | string[]) => {
		setForm((prev) => ({
			...prev,
			stdio_config: {
				command: "",
				args: [],
				envs: [],
				...(prev.stdio_config || {}),
				[field]: value,
			},
		}));
	};

	const handleHeadersChange = (value: Record<string, EnvVar>) => {
		setForm((prev) => ({ ...prev, headers: value }));
	};

	const handleConnectionStringChange = (value: EnvVar) => {
		setForm((prev) => ({
			...prev,
			connection_string: value,
		}));
	};

	const handleOAuthConfigChange = (field: keyof OAuthConfig, value: string | string[]) => {
		setForm((prev) => ({
			...prev,
			oauth_config: {
				...(prev.oauth_config || emptyOAuthConfig),
				[field]: value,
			},
		}));
	};

	// Validate headers format
	const validateHeaders = (): string | null => {
		if ((form.connection_type === "http" || form.connection_type === "sse") && form.auth_type === "headers" && form.headers) {
			// Ensure all EnvVar values have either a value or env_var
			for (const [key, envVar] of Object.entries(form.headers)) {
				if (!envVar.value && !envVar.env_var) {
					return `Header "${key}" must have a value`;
				}
			}
		}
		return null;
	};

	const headersValidationError = validateHeaders();

	// Get the connection string value for validation
	const connectionStringValue = form.connection_string?.value || "";

	const validator = new Validator([
		// Name validation
		Validator.required(form.name?.trim(), "Server name is required"),
		Validator.pattern(form.name || "", /^[a-zA-Z0-9_]+$/, "Server name can only contain letters, numbers, and underscores"),
		Validator.custom(!(form.name || "").includes("-"), "Server name cannot contain hyphens"),
		Validator.custom(!(form.name || "").includes(" "), "Server name cannot contain spaces"),
		Validator.custom((form.name || "").length === 0 || !/^[0-9]/.test(form.name || ""), "Server name cannot start with a number"),
		Validator.minLength(form.name || "", 3, "Server name must be at least 3 characters"),
		Validator.maxLength(form.name || "", 50, "Server name cannot exceed 50 characters"),

		// Connection type specific validation
		...(form.connection_type === "http" || form.connection_type === "sse"
			? [
					Validator.required(connectionStringValue?.trim(), "Connection URL is required"),
					Validator.pattern(
						connectionStringValue,
						/^((https?:\/\/.+)|(env\.[A-Z_]+))$/,
						"Connection URL must start with http://, https://, or be an environment variable (env.VAR_NAME)",
					),
					...(headersValidationError ? [Validator.custom(false, headersValidationError)] : []),
				]
			: []),

		// STDIO validation
		...(form.connection_type === "stdio"
			? [
					Validator.required(form.stdio_config?.command?.trim(), "Command is required for STDIO connections"),
					Validator.pattern(form.stdio_config?.command || "", /^[^<>|&;]+$/, "Command cannot contain special shell characters"),
				]
			: []),

		// OAuth validation
		...(form.auth_type === "oauth" || form.auth_type === "per_user_oauth"
			? [
					// Client ID is optional if provider supports dynamic registration (RFC 7591)
					// URLs are optional (will be discovered), but if provided must be valid
					...(form.oauth_config?.authorize_url
						? [Validator.pattern(form.oauth_config.authorize_url, /^https?:\/\/.+$/, "Authorize URL must start with http:// or https://")]
						: []),
					...(form.oauth_config?.token_url
						? [Validator.pattern(form.oauth_config.token_url, /^https?:\/\/.+$/, "Token URL must start with http:// or https://")]
						: []),
					...(form.oauth_config?.registration_url
						? [
								Validator.pattern(
									form.oauth_config.registration_url,
									/^https?:\/\/.+$/,
									"Registration URL must start with http:// or https://",
								),
							]
						: []),
				]
			: []),
	]);

	const handleSubmit = async () => {
		// Validate before submitting
		if (!validator.isValid()) {
			toast({
				title: "Validation Error",
				description: validator.getFirstError() || "Please fix validation errors",
				variant: "destructive",
			});
			return;
		}

		setIsLoading(true);

		// Prepare the payload
		const payload: CreateMCPClientRequest = {
			...form,
			stdio_config:
				form.connection_type === "stdio"
					? {
							command: form.stdio_config?.command || "",
							args: parseArrayFromText(argsText),
							envs: parseArrayFromText(envsText),
						}
					: undefined,
			oauth_config:
				form.auth_type === "oauth" || form.auth_type === "per_user_oauth"
					? {
							client_id: form.oauth_config?.client_id || "", // Can be empty for dynamic registration
							client_secret: form.oauth_config?.client_secret || undefined,
							authorize_url: form.oauth_config?.authorize_url || undefined,
							token_url: form.oauth_config?.token_url || undefined,
							registration_url: form.oauth_config?.registration_url || undefined,
							scopes: scopesText.trim() ? parseArrayFromText(scopesText) : undefined,
							server_url: form.connection_string?.value || undefined, // Set server_url from connection_string
						}
					: undefined,
			headers: form.auth_type === "headers" && form.headers && Object.keys(form.headers).length > 0 ? form.headers : undefined,
			tools_to_execute: ["*"],
		};

		try {
			const response = await createMCPClient(payload).unwrap();

			// Check if OAuth flow was initiated
			if (response.status === "pending_oauth" && response.authorize_url) {
				setIsLoading(false);
				// Open OAuth authorizer popup
				setOauthFlow({
					authorizeUrl: response.authorize_url,
					oauthConfigId: response.oauth_config_id,
					mcpClientId: response.mcp_client_id,
					isPerUserOauth: form.auth_type === "per_user_oauth",
				});
			} else {
				setIsLoading(false);
				toast({
					title: "Success",
					description: "Server created",
				});
				onSaved();
				onClose();
			}
		} catch (error) {
			setIsLoading(false);
			toast({ title: "Error", description: getErrorMessage(error), variant: "destructive" });
		}
	};

	return (
		<Sheet open={open} onOpenChange={(open) => !open && onClose()}>
			<SheetContent className="flex w-full flex-col overflow-x-hidden px-4 pb-8">
				<SheetHeader className="flex flex-col items-start px-4 pt-8">
					<SheetTitle>New MCP Server</SheetTitle>
					<SheetDescription>Configure and connect to a new Model Context Protocol server.</SheetDescription>
				</SheetHeader>

				<div className="space-y-4 px-4">
					<div className="space-y-2">
						<Label htmlFor="client-name">Name</Label>
						<Input
							id="client-name"
							data-testid="client-name-input"
							value={form.name}
							onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleChange("name", e.target.value)}
							placeholder="Server name"
							maxLength={50}
						/>
					</div>

					<div className="w-full space-y-2">
						<Label>Connection Type</Label>
						<Select value={form.connection_type} onValueChange={(value: MCPConnectionType) => handleChange("connection_type", value)}>
							<SelectTrigger className="w-full" data-testid="connection-type-select">
								<SelectValue placeholder="Select connection type" />
							</SelectTrigger>
							<SelectContent>
								<SelectItem value="http" data-testid="connection-type-http">
									HTTP (Streamable)
								</SelectItem>
								<SelectItem value="sse" data-testid="connection-type-sse">
									Server-Sent Events (SSE)
								</SelectItem>
								<SelectItem value="stdio" data-testid="connection-type-stdio">
									STDIO
								</SelectItem>
							</SelectContent>
						</Select>
					</div>

					<div className="flex items-center justify-between space-x-2 rounded-lg border p-4">
						<div className="flex items-center gap-2">
							<Label htmlFor="code-mode">Code Mode Server</Label>
							<TooltipProvider>
								<Tooltip>
									<TooltipTrigger asChild>
										<a
											href="https://docs.getbifrost.ai/mcp/code-mode"
											target="_blank"
											rel="noopener noreferrer"
											data-testid="code-mode-link-help"
											className="text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded focus-visible:ring-2 focus-visible:outline-none"
											aria-label="Learn more about Code Mode"
										>
											<Info className="h-4 w-4 cursor-help" />
										</a>
									</TooltipTrigger>
									<TooltipContent>
										<p>Learn more about Code Mode</p>
									</TooltipContent>
								</Tooltip>
							</TooltipProvider>
						</div>
						<Switch
							id="code-mode"
							data-testid="code-mode-switch"
							checked={form.is_code_mode_client || false}
							onCheckedChange={(checked) => handleChange("is_code_mode_client", checked)}
						/>
					</div>

					<div className="flex items-center justify-between space-x-2 rounded-lg border p-4">
						<div className="flex items-center gap-2">
							<Label htmlFor="ping-available">Ping Available for Health Check</Label>
							<TooltipProvider>
								<Tooltip>
									<TooltipTrigger asChild>
										<Info className="text-muted-foreground h-4 w-4 cursor-help" />
									</TooltipTrigger>
									<TooltipContent className="max-w-xs">
										<p>
											Enable to use lightweight ping method for health checks. Disable if your MCP server doesn't support ping - will use
											listTools instead.
										</p>
									</TooltipContent>
								</Tooltip>
							</TooltipProvider>
						</div>
						<Switch
							id="ping-available"
							checked={form.is_ping_available === true}
							onCheckedChange={(checked) => handleChange("is_ping_available", checked)}
						/>
					</div>

					{(form.connection_type === "http" || form.connection_type === "sse") && (
						<>
							<div className="space-y-2">
								<div className="flex w-fit items-center gap-1">
									<Label>Connection URL</Label>
									<TooltipProvider>
										<Tooltip>
											<TooltipTrigger asChild>
												<span>
													<Info className="text-muted-foreground ml-1 h-3 w-3" />
												</span>
											</TooltipTrigger>
											<TooltipContent className="max-w-fit">
												<p>
													Use <code className="rounded bg-neutral-100 px-1 py-0.5 text-neutral-800">env.&lt;VAR&gt;</code> to read the value
													from an environment variable.
												</p>
											</TooltipContent>
										</Tooltip>
									</TooltipProvider>
								</div>

								<EnvVarInput
									value={form.connection_string}
									onChange={handleConnectionStringChange}
									placeholder="http://your-mcp-server:3000 or env.MCP_SERVER_URL"
									data-testid="connection-url-input"
								/>
							</div>
							<div className="w-full space-y-2">
								<Label>Authentication Type</Label>
								<Select value={form.auth_type} onValueChange={(value: MCPAuthType) => handleChange("auth_type", value)}>
									<SelectTrigger className="w-full" data-testid="auth-type-select">
										<SelectValue placeholder="Select authentication type" />
									</SelectTrigger>
									<SelectContent>
										<SelectItem value="none" data-testid="auth-type-none">
											None
										</SelectItem>
										<SelectItem value="headers" data-testid="auth-type-headers">
											Headers
										</SelectItem>
										<SelectItem value="oauth" data-testid="auth-type-oauth">
											OAuth 2.0
										</SelectItem>
										<SelectItem value="per_user_oauth" data-testid="auth-type-per-user-oauth">
											Per-User OAuth 2.0
										</SelectItem>
									</SelectContent>
								</Select>
							</div>
							{form.auth_type === "headers" && (
								<div className="space-y-2" data-testid="mcp-headers-table">
									<HeadersTable
										value={form.headers || {}}
										onChange={handleHeadersChange}
										keyPlaceholder="Header name"
										valuePlaceholder="Header value"
										label="Headers"
										useEnvVarInput
									/>
									{headersValidationError && <p className="text-destructive text-xs">{headersValidationError}</p>}
								</div>
							)}

							{(form.auth_type === "oauth" || form.auth_type === "per_user_oauth") && (
								<>
									<div className="space-y-2">
										<div className="flex items-center gap-2">
											<Label>OAuth Client ID (optional)</Label>
											<TooltipProvider>
												<Tooltip>
													<TooltipTrigger asChild>
														<Info className="text-muted-foreground h-4 w-4 cursor-help" />
													</TooltipTrigger>
													<TooltipContent className="max-w-xs">
														<p>
															Leave empty to use Dynamic Client Registration (RFC 7591). Bifrost will automatically register with the OAuth
															provider if supported.
														</p>
													</TooltipContent>
												</Tooltip>
											</TooltipProvider>
										</div>
										<Input
											value={form.oauth_config?.client_id || ""}
											onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleOAuthConfigChange("client_id", e.target.value)}
											placeholder="your-client-id (auto-generated if empty)"
										/>
										<p className="text-muted-foreground text-xs">
											Will be auto-generated via dynamic registration if left empty and provider supports it
										</p>
									</div>
									<div className="space-y-2">
										<Label>OAuth Client Secret (optional for PKCE)</Label>
										<Input
											type="password"
											value={form.oauth_config?.client_secret || ""}
											onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleOAuthConfigChange("client_secret", e.target.value)}
											placeholder="your-client-secret"
										/>
										<p className="text-muted-foreground text-xs">Leave empty for public clients using PKCE</p>
									</div>
									<div className="space-y-2">
										<Label>Authorization URL (optional, auto-discovered)</Label>
										<Input
											value={form.oauth_config?.authorize_url || ""}
											onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleOAuthConfigChange("authorize_url", e.target.value)}
											placeholder="https://provider.com/oauth/authorize"
										/>
										<p className="text-muted-foreground text-xs">Will be discovered from server if not provided</p>
									</div>
									<div className="space-y-2">
										<Label>Token URL (optional, auto-discovered)</Label>
										<Input
											value={form.oauth_config?.token_url || ""}
											onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleOAuthConfigChange("token_url", e.target.value)}
											placeholder="https://provider.com/oauth/token"
										/>
										<p className="text-muted-foreground text-xs">Will be discovered from server if not provided</p>
									</div>
									<div className="space-y-2">
										<Label>Registration URL (optional, auto-discovered)</Label>
										<Input
											value={form.oauth_config?.registration_url || ""}
											onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleOAuthConfigChange("registration_url", e.target.value)}
											placeholder="https://provider.com/oauth/register"
										/>
										<p className="text-muted-foreground text-xs">For dynamic client registration, will be discovered if not provided</p>
									</div>
									<div className="space-y-2">
										<Label>Scopes (optional, comma-separated)</Label>
										<Input
											value={scopesText}
											onChange={(e: React.ChangeEvent<HTMLInputElement>) => setScopesText(e.target.value)}
											placeholder="read, write, admin"
										/>
										<p className="text-muted-foreground text-xs">Will be discovered from server if not provided</p>
									</div>
								</>
							)}
						</>
					)}

					{form.connection_type === "stdio" && (
						<>
							<div className="rounded-lg border border-amber-200 bg-amber-50 p-3">
								<div className="flex items-start gap-2">
									<Info className="mt-0.5 h-4 w-4 flex-shrink-0 text-amber-700" />
									<div className="flex-1">
										<p className="text-xs font-medium text-amber-900">Docker Notice</p>
										<p className="mt-0.5 text-xs text-amber-800">
											If not using the official Bifrost Docker image, STDIO connections may not work if required commands (npx, python,
											etc.) aren't installed. You can safely ignore this if running locally or using a custom image with the necessary
											dependencies.
										</p>
									</div>
								</div>
							</div>
							<div className="space-y-2">
								<Label>Command</Label>
								<Input
									value={form.stdio_config?.command || ""}
									onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleStdioConfigChange("command", e.target.value)}
									placeholder="node, python, /path/to/executable"
									data-testid="stdio-command-input"
								/>
							</div>
							<div className="space-y-2">
								<Label>Arguments (comma-separated)</Label>
								<Input
									value={argsText}
									onChange={(e: React.ChangeEvent<HTMLInputElement>) => setArgsText(e.target.value)}
									placeholder="--port, 3000, --config, config.json"
									data-testid="stdio-args-input"
								/>
							</div>
							<div className="space-y-2">
								<Label>Environment Variables (comma-separated)</Label>
								<Input
									value={envsText}
									onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEnvsText(e.target.value)}
									placeholder="API_KEY, DATABASE_URL"
									data-testid="stdio-envs-input"
								/>
							</div>
						</>
					)}
				</div>
				{/* Form Footer */}
				<div className="dark:bg-card border-border bg-white px-4 py-6">
					<div className="flex justify-end gap-2">
						<Button type="button" variant="outline" onClick={onClose} disabled={isLoading} data-testid="cancel-client-btn">
							Cancel
						</Button>
						<TooltipProvider>
							<Tooltip>
								<TooltipTrigger asChild>
									<span className="inline-block">
										<Button
											onClick={handleSubmit}
											disabled={!validator.isValid() || isLoading || !hasCreateMCPClientAccess}
											isLoading={isLoading}
											data-testid="save-client-btn"
										>
											Create
										</Button>
									</span>
								</TooltipTrigger>
								{(!validator.isValid() || !hasCreateMCPClientAccess) && (
									<TooltipContent>
										<p>
											{!hasCreateMCPClientAccess
												? "You don't have permission to perform this action"
												: validator.getFirstError() || "Please fix validation errors"}
										</p>
									</TooltipContent>
								)}
							</Tooltip>
						</TooltipProvider>
					</div>
				</div>
			</SheetContent>

			{/* OAuth Authorizer Popup */}
			{oauthFlow && (
				<OAuth2Authorizer
					open={!!oauthFlow}
					onClose={() => {
						setOauthFlow(null);
						onClose();
					}}
					onSuccess={() => {
						toast({
							title: "Success",
							description: "MCP server connected with OAuth",
						});
						onSaved();
						setOauthFlow(null);
						onClose();
					}}
					onError={(error) => {
						toast({
							title: "OAuth Error",
							description: error,
							variant: "destructive",
						});
						setOauthFlow(null);
					}}
					authorizeUrl={oauthFlow.authorizeUrl}
					oauthConfigId={oauthFlow.oauthConfigId}
					mcpClientId={oauthFlow.mcpClientId}
					isPerUserOauth={oauthFlow.isPerUserOauth}
				/>
			)}
		</Sheet>
	);
};

export default ClientForm;