"use client";
import { useState, useMemo } from "react";
import {
  MCPServerConfig,
  MCPRemoteConfigZodSchema,
  MCPStdioConfigZodSchema,
} from "app-types/mcp";
import { Input } from "./ui/input";
import { Button } from "./ui/button";
import { Label } from "./ui/label";
import { Textarea } from "./ui/textarea";
import JsonView from "./ui/json-view";
import { toast } from "sonner";
import { safe } from "ts-safe";
import { useRouter } from "next/navigation";
import { createDebounce, fetcher, isNull, safeJSONParse } from "lib/utils";
import { handleErrorWithToast } from "ui/shared-toast";
import { mutate } from "swr";
import { Loader } from "lucide-react";
import {
  isMaybeMCPServerConfig,
  isMaybeRemoteConfig,
} from "lib/ai/mcp/is-mcp-config";

import { Alert, AlertDescription, AlertTitle } from "ui/alert";
import { z } from "zod";
import { useTranslations } from "next-intl";
import { existMcpClientByServerNameAction } from "@/app/api/mcp/actions";

interface MCPEditorProps {
  initialConfig?: MCPServerConfig;
  name?: string;
  id?: string;
}

const STDIO_ARGS_ENV_PLACEHOLDER = `/** STDIO Example */
{
  "command": "node", 
  "args": ["index.js"],
  "env": {
    "OPENAI_API_KEY": "sk-...",
  }
}

/** SSE,Streamable HTTP Example */
{
  "url": "https://api.example.com",
  "headers": {
    "Authorization": "Bearer sk-..."
  }
}

/** Standard mcpServers JSON Example */
{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["index.js"]
    }
  }
}`;

// Helper to check and extract standard mcpServers configurations
const getMcpServersFromObject = (obj: any): Record<string, MCPServerConfig> | null => {
  if (!obj || typeof obj !== "object") return null;
  const servers = obj.mcpServers || obj;
  if (typeof servers !== "object" || Array.isArray(servers)) return null;

  const keys = Object.keys(servers);
  if (keys.length === 0) return null;

  // Verify all keys represent valid server configs
  const allValid = keys.every(key => {
    const srv = servers[key];
    return srv && typeof srv === "object" && isMaybeMCPServerConfig(srv);
  });

  return allValid ? servers : null;
};

// Helper to rewrite temp path to permanent path for loop-engineering
const rewriteConfigPaths = (cfg: any): any => {
  try {
    let str = JSON.stringify(cfg);
    str = str.replace(/\/tmp\/loop-eng\/loop-engineering/g, "/app/loop-engineering");
    return JSON.parse(str);
  } catch {
    return cfg;
  }
};

export default function MCPEditor({
  initialConfig,
  name: initialName,
  id,
}: MCPEditorProps) {
  const t = useTranslations();
  const shouldInsert = useMemo(() => isNull(id), [id]);

  const [isLoading, setIsLoading] = useState(false);
  const [jsonError, setJsonError] = useState<string | null>(null);
  const [nameError, setNameError] = useState<string | null>(null);

  const errorDebounce = useMemo(() => createDebounce(), []);

  // State for form fields
  const [name, setName] = useState<string>(initialName ?? "");
  const router = useRouter();
  const [config, setConfig] = useState<MCPServerConfig>(
    initialConfig as MCPServerConfig,
  );
  const [jsonString, setJsonString] = useState<string>(
    initialConfig ? JSON.stringify(initialConfig, null, 2) : "",
  );

  // Name validation schema
  const nameSchema = z.string().regex(/^[a-zA-Z0-9\-]+$/, {
    message: t("MCP.nameMustContainOnlyAlphanumericCharactersAndHyphens"),
  });

  const validateName = (nameValue: string): boolean => {
    const result = nameSchema.safeParse(nameValue);
    if (!result.success) {
      setNameError(
        t("MCP.nameMustContainOnlyAlphanumericCharactersAndHyphens"),
      );
      return false;
    }
    setNameError(null);
    return true;
  };

  const isMulti = useMemo(() => {
    return getMcpServersFromObject(config) !== null;
  }, [config]);

  const saveDisabled = useMemo(() => {
    return (
      (!isMulti && name.trim() === "") ||
      isLoading ||
      !!jsonError ||
      (!isMulti && !!nameError) ||
      !(isMaybeMCPServerConfig(config) || isMulti)
    );
  }, [isLoading, jsonError, nameError, config, name, isMulti]);

  // Validate
  const validateConfig = (jsonConfig: unknown): boolean => {
    const isMultiConfig = getMcpServersFromObject(jsonConfig) !== null;
    if (isMultiConfig) return true;
    const result = isMaybeRemoteConfig(jsonConfig)
      ? MCPRemoteConfigZodSchema.safeParse(jsonConfig)
      : MCPStdioConfigZodSchema.safeParse(jsonConfig);
    if (!result.success) {
      handleErrorWithToast(result.error, "mcp-editor-error");
    }
    return result.success;
  };

  // Handle save button click
  const handleSave = async () => {
    const mcpServers = getMcpServersFromObject(config);
    if (mcpServers) {
      setIsLoading(true);
      try {
        for (const [srvName, srvConfig] of Object.entries(mcpServers)) {
          // Validate name
          const nameResult = nameSchema.safeParse(srvName);
          if (!nameResult.success) {
            throw new Error(`Invalid server name "${srvName}": ${nameResult.error.issues[0].message}`);
          }

          // Validate individual config schema
          const configResult = isMaybeRemoteConfig(srvConfig)
            ? MCPRemoteConfigZodSchema.safeParse(srvConfig)
            : MCPStdioConfigZodSchema.safeParse(srvConfig);
          if (!configResult.success) {
            throw new Error(`Invalid config for server "${srvName}": ${configResult.error.issues[0].message}`);
          }

          if (shouldInsert) {
            const exist = await existMcpClientByServerNameAction(srvName);
            if (exist) {
              throw new Error(`Server "${srvName}" already exists`);
            }
          }
        }

        // Save each server
        for (const [srvName, srvConfig] of Object.entries(mcpServers)) {
          await fetcher("/api/mcp", {
            method: "POST",
            body: JSON.stringify({
              name: srvName,
              config: rewriteConfigPaths(srvConfig),
            }),
          });
        }

        toast.success("All MCP configurations saved successfully");
        mutate("/api/mcp/list");
        router.push("/mcp");
      } catch (err: any) {
        handleErrorWithToast(err);
      } finally {
        setIsLoading(false);
      }
      return;
    }

    // Perform validation
    if (!validateConfig(config)) return;
    if (!name) {
      return handleErrorWithToast(
        new Error(t("MCP.nameIsRequired")),
        "mcp-editor-error",
      );
    }

    if (!validateName(name)) {
      return handleErrorWithToast(
        new Error(t("MCP.nameMustContainOnlyAlphanumericCharactersAndHyphens")),
        "mcp-editor-error",
      );
    }

    safe(() => setIsLoading(true))
      .map(async () => {
        if (shouldInsert) {
          const exist = await existMcpClientByServerNameAction(name);
          if (exist) {
            throw new Error(t("MCP.nameAlreadyExists"));
          }
        }
      })
      .map(() =>
        fetcher("/api/mcp", {
          method: "POST",
          body: JSON.stringify({
            name,
            config: rewriteConfigPaths(config),
            id,
          }),
        }),
      )
      .ifOk(() => {
        toast.success(t("MCP.configurationSavedSuccessfully"));
        mutate("/api/mcp/list");
        router.push("/mcp");
      })
      .ifFail(handleErrorWithToast)
      .watch(() => setIsLoading(false));
  };

  const handleConfigChange = (data: string) => {
    setJsonString(data);
    const result = safeJSONParse(data);
    errorDebounce.clear();
    if (result.success) {
      setConfig(result.value as MCPServerConfig);
      setJsonError(null);
    } else if (data.trim() !== "") {
      errorDebounce(() => {
        setJsonError(
          (result.error as Error)?.message ??
            JSON.stringify(result.error, null, 2),
        );
      }, 1000);
    }
  };

  return (
    <>
      <div className="flex flex-col space-y-6">
        {/* Name field */}
        <div className="space-y-2">
          <Label htmlFor="name">Name</Label>

          <Input
            id="name"
            value={isMulti ? "Multiple Servers Detected (Names read from JSON)" : name}
            disabled={!shouldInsert || isMulti}
            onChange={(e) => {
              setName(e.target.value);
              if (e.target.value) validateName(e.target.value);
            }}
            placeholder={t("MCP.enterMcpServerName")}
            className={nameError && !isMulti ? "border-destructive" : ""}
          />
          {nameError && !isMulti && <p className="text-xs text-destructive">{nameError}</p>}
        </div>
        <div className="space-y-4">
          <div className="space-y-2">
            <Label htmlFor="config">Config</Label>
          </div>

          {/* Split view for config editor */}
          <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
            {/* Left side: Textarea for editing */}
            <div className="space-y-2">
              <Textarea
                id="config-editor"
                value={jsonString}
                onChange={(e) => handleConfigChange(e.target.value)}
                data-testid="mcp-config-editor"
                className="font-mono h-[40vh] resize-none overflow-y-auto"
                placeholder={STDIO_ARGS_ENV_PLACEHOLDER}
              />
            </div>

            {/* Right side: JSON view */}
            <div className="space-y-2 hidden sm:block">
              <div className="border border-input rounded-md p-4 h-[40vh] overflow-auto relative bg-secondary">
                <Label
                  htmlFor="config-view"
                  className="text-xs text-muted-foreground mb-2"
                >
                  preview
                </Label>
                <JsonView
                  data={config}
                  initialExpandDepth={3}
                  data-testid="mcp-config-view"
                />
                {jsonError && jsonString && (
                  <div className="absolute w-full bottom-0 right-0 px-2 pb-2 animate-in fade-in-0 duration-300">
                    <Alert variant="destructive" className="border-destructive">
                      <AlertTitle className="text-xs font-semibold">
                        Parsing Error
                      </AlertTitle>
                      <AlertDescription className="text-xs">
                        {jsonError}
                      </AlertDescription>
                    </Alert>
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>

        {/* Save button */}
        <Button onClick={handleSave} className="w-full" disabled={saveDisabled}>
          {isLoading ? (
            <Loader className="size-4 animate-spin" />
          ) : (
            <span className="font-bold">{t("MCP.saveConfiguration")}</span>
          )}
        </Button>
      </div>
    </>
  );
}
