/**
 * Launch Modal Component
 * Launch service instance from artifact
 * Supports Values Schema for dynamic form generation
 */
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { Rocket, AlertCircle, FileCode, FormInput, Sparkles, SlidersHorizontal } from "lucide-react";
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
import { useToast } from "@/shared";
import { createInstance, listClusters, getValuesSchema, getValuesYaml } from "@/api";
import type { CreateInstanceRequest, ClusterResponse } from "@/api";
import { useAuth } from "@/app/providers";
import { isAdminUser } from "@/app/providers/auth-model";
import { ClusterErrors, InstanceErrors, SuccessMessages, ValidationErrors, formatApiError } from "@/shared/utils";
import { 
  Modal, 
  Button, 
  FormField, 
  Input, 
  DropdownSelect,
  Textarea,
  LoadingState,
  Badge,
  SchemaFormGenerator
} from "@/shared/components";
import type { ArtifactCategory } from "../utils/artifactType";
import type { JsonSchema } from "@/shared/components/form/SchemaFormGenerator";

type NamespacePolicyValue =
  | string
  | {
      allowedNamespaces?: string[];
      defaultNamespace?: string;
      namespace?: string;
      readOnly?: boolean;
      readonly?: boolean;
    };

type ClusterWithNamespacePolicy = ClusterResponse & {
  allowedNamespaces?: string[];
  namespacePolicy?: NamespacePolicyValue;
  namespaceReadOnly?: boolean;
  namespaceReadonly?: boolean;
  defaultNamespace?: string;
};

interface LaunchModalProps {
  isOpen: boolean;
  onClose: () => void;
  registryId: string;
  repositoryName: string;
  tag: string;
  artifactType: ArtifactCategory;
}

export const LaunchModal: React.FC<LaunchModalProps> = ({
  isOpen,
  onClose,
  registryId,
  repositoryName,
  tag,
  artifactType,
}) => {
  const navigate = useNavigate();
  const { user } = useAuth();
  const isAdmin = isAdminUser(user);
  const { success, error: toastError, info: toastInfo } = useToast();
  
  const [clusters, setClusters] = useState<ClusterWithNamespacePolicy[]>([]);
  const [loadingClusters, setLoadingClusters] = useState(false);
  
  // Form fields
  const [clusterId, setClusterId] = useState("");
  const [namespace, setNamespace] = useState(isAdmin ? "default" : (user?.namespace || "default"));
  const [instanceName, setInstanceName] = useState("");
  const [description, setDescription] = useState("");
  
  // Values Schema support
  const [valuesSchema, setValuesSchema] = useState<JsonSchema | null>(null);
  const [defaultValuesYaml, setDefaultValuesYaml] = useState("");
  const [loadingSchema, setLoadingSchema] = useState(false);
  const [inputMethod, setInputMethod] = useState<'quick' | 'form' | 'yaml'>('quick');
  const [valuesForm, setValuesForm] = useState<Record<string, any>>({});
  const [valuesYaml, setValuesYaml] = useState("");
  const [yamlError, setYamlError] = useState<string | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const schemaDefaults = React.useMemo(
    () => valuesSchema ? extractSchemaDefaults(valuesSchema) : {},
    [valuesSchema]
  );
  const schemaDefaultCount = React.useMemo(
    () => countLeafValues(schemaDefaults),
    [schemaDefaults]
  );
  const isVllmChart = /(^|[/_-])vllm([/_-]|$)|vllm-serve/i.test(repositoryName);
  const selectedCluster = React.useMemo(
    () => clusters.find((cluster) => cluster.id === clusterId),
    [clusters, clusterId]
  );
  const namespaceAccess = React.useMemo(
    () => getNamespaceAccess(selectedCluster, user?.namespace, user?.role),
    [selectedCluster, user?.namespace, user?.role]
  );

  // Load clusters and schema on mount
  useEffect(() => {
    if (isOpen) {
      loadClusters();
      loadValuesSchema();
      loadDefaultValuesYaml();
    }
  }, [isOpen, registryId, repositoryName, tag]);

  const loadClusters = async () => {
    setLoadingClusters(true);
    try {
      const data = await listClusters();
      const normalizedClusters = data as ClusterWithNamespacePolicy[];
      setClusters(normalizedClusters);
      const preferredCluster =
        normalizedClusters.find((cluster) => cluster.id && cluster.id === user?.defaultClusterId) ??
        normalizedClusters.find((cluster) => typeof cluster.id === "string" && cluster.id.length > 0);
      if (preferredCluster?.id) {
        setClusterId(preferredCluster.id);
      } else {
        setClusterId("");
      }
    } catch (err) {
      toastError(formatApiError(err) || ClusterErrors.LOAD_FAILED);
      console.error(err);
    } finally {
      setLoadingClusters(false);
    }
  };

  const loadDefaultValuesYaml = async () => {
    try {
      const response = await getValuesYaml({ registryId, repositoryName, reference: tag });
      setDefaultValuesYaml(typeof response.valuesYaml === "string" ? response.valuesYaml : "");
    } catch (err) {
      console.error("[LaunchModal] Failed to load chart values.yaml:", err);
      setDefaultValuesYaml("");
    }
  };

  const loadValuesSchema = async () => {
    setLoadingSchema(true);
    try {
      const schemaResponse = await getValuesSchema({ registryId, repositoryName, reference: tag });
      const normalizedSchema = extractJsonSchema(schemaResponse);
      setValuesSchema(normalizedSchema);
      
      if (normalizedSchema) {
        console.log(`[LaunchModal] Loaded values schema with ${Object.keys(normalizedSchema.properties ?? {}).length} properties`);
      } else {
        console.log('[LaunchModal] No values schema available; keeping quick launch with optional YAML overrides');
      }
    } catch (err) {
      console.error('[LaunchModal] Failed to load values schema:', err);
      setValuesSchema(null);
    } finally {
      setLoadingSchema(false);
    }
  };

  const resetFormState = () => {
    setInstanceName("");
    setDescription("");
    setValuesYaml("");
    setYamlError(null);
    setValuesForm({});
    setNamespace(isAdmin ? "default" : (user?.namespace || "default"));
    setInputMethod("quick");
  };

  useEffect(() => {
    if (!selectedCluster) {
      return;
    }
    const access = getNamespaceAccess(selectedCluster, user?.namespace, user?.role);
    if (access.defaultNamespace && namespace !== access.defaultNamespace) {
      setNamespace(access.defaultNamespace);
    } else if (!access.defaultNamespace && user?.namespace && namespace !== user.namespace) {
      setNamespace(user.namespace);
    }
  }, [selectedCluster, namespace, user?.namespace]);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    if (artifactType !== "chart") {
      toastError("Only Helm chart artifacts can be launched.");
      return;
    }

    if (!clusterId) {
      toastError(ValidationErrors.REQUIRED_FIELD("Cluster"));
      return;
    }

    if (!instanceName.trim()) {
      toastError(ValidationErrors.REQUIRED_FIELD("Instance name"));
      return;
    }

    if (!namespace.trim()) {
      toastError(ValidationErrors.REQUIRED_FIELD("Namespace"));
      return;
    }
    if (
      namespaceAccess.allowedNamespaces.length > 0 &&
      !namespaceAccess.allowedNamespaces.includes(namespace.trim())
    ) {
      toastError("Selected namespace is not allowed for this cluster.");
      return;
    }

    let valuesObj: Record<string, any> = {};
    let normalizedValuesYaml = "";
    if (inputMethod === "form" && Object.keys(valuesForm).length > 0) {
      valuesObj = pruneEmptyValues(valuesForm);
    } else if (inputMethod === "yaml" && valuesYaml.trim()) {
      try {
        parseValuesYaml(valuesYaml);
        normalizedValuesYaml = valuesYaml.trim();
      } catch (err) {
        console.error(err);
        const message = "Invalid YAML format. Please check your values.";
        setYamlError(message);
        toastError(message);
        return;
      }
    }

    const request: CreateInstanceRequest = {
      name: instanceName.trim(),
      namespace: namespace.trim(),
      registryId,
      repository: repositoryName,
      tag,
      ...(description.trim() ? { description: description.trim() } : {}),
      ...(Object.keys(valuesObj).length > 0 ? { values: valuesObj } : {}),
      ...(normalizedValuesYaml ? { valuesYaml: normalizedValuesYaml } : {}),
    };

    toastInfo("Launching instance...", {
      title: "Launch Instance",
      durationMs: 1800,
      mergeKey: `instance-launch-${registryId}-${repositoryName}-${tag}`,
    });

    setSubmitting(true);
    try {
      await createInstance({ clusterId }, request);
      success(SuccessMessages.INSTANCE_DEPLOYED);
      resetFormState();
      onClose();
      navigate("/artifact/instances");
    } catch (err) {
      toastError(formatApiError(err) || InstanceErrors.DEPLOY_FAILED);
      console.error(err);
    } finally {
      setSubmitting(false);
    }
  };

  if (!isOpen) return null;

  return (
    <Modal
      open={isOpen}
      onClose={onClose}
      title="Launch Instance"
      icon={Rocket}
      iconColor="text-green-400"
      size="lg"
      footer={
        <>
          <Button
            type="button"
            variant="secondary"
            onClick={onClose}
          >
            Cancel
          </Button>
          <Button
            type="submit"
            variant="success"
            icon={Rocket}
            onClick={handleSubmit}
            loading={submitting}
            disabled={clusters.length === 0 || artifactType !== "chart" || submitting || Boolean(yamlError)}
          >
            Launch
          </Button>
        </>
      }
    >
      <div className="space-y-1 mb-4">
        <p className="text-sm text-slate-500">
          {repositoryName}:{tag}
        </p>
      </div>

      <form onSubmit={handleSubmit} className="space-y-4">
          {/* Cluster Selection */}
          <FormField label="Target Cluster" required>
            {loadingClusters ? (
              <div className="text-sm text-slate-500">Loading clusters...</div>
            ) : clusters.length === 0 ? (
              <div className="flex items-center gap-2 p-3 bg-amber-50 border border-amber-200 rounded-lg text-amber-700 text-sm">
                <AlertCircle className="w-4 h-4" />
                <span>No clusters available. Please add a cluster first.</span>
              </div>
            ) : (
              <DropdownSelect
                value={clusterId}
                onChange={(value) => setClusterId(value)}
                options={clusters
                  .filter((cluster): cluster is ClusterResponse & { id: string } => Boolean(cluster.id))
                  .map((cluster) => ({
                    value: cluster.id,
                    label: cluster.name || cluster.id,
                  }))}
                placeholder="Select a cluster"
                required
              />
            )}
          </FormField>

          {/* Instance Name */}
          <FormField
            label="Instance Name"
            required
            help="Lowercase alphanumeric characters, '-' or '.'"
          >
            <Input
              type="text"
              value={instanceName}
              onChange={(e) => setInstanceName(e.target.value)}
              placeholder="my-app"
              required
            />
          </FormField>

          {/* Namespace */}
          <FormField label="Namespace" required>
            {isAdmin ? (
              <>
                <Input
                  type="text"
                  value={namespace}
                  onChange={(e) => setNamespace(e.target.value)}
                  placeholder="default"
                  required
                />
                <div className="mt-2 flex items-center gap-2 rounded-md border border-blue-100 bg-blue-50 px-3 py-2 text-xs text-blue-700">
                  <AlertCircle className="h-3.5 w-3.5" />
                  Admin can deploy to any namespace except system namespaces (kube-system, kube-public, kube-node-lease).
                </div>
              </>
            ) : namespaceAccess.allowedNamespaces.length > 0 ? (
              <DropdownSelect
                value={namespace}
                onChange={(value) => setNamespace(value)}
                options={namespaceAccess.allowedNamespaces.map((allowedNamespace) => ({
                  value: allowedNamespace,
                  label: allowedNamespace,
                }))}
                placeholder="Select a namespace"
                required
              />
            ) : (
              <Input
                type="text"
                value={namespace}
                onChange={(e) => setNamespace(e.target.value)}
                placeholder="default"
                required
                disabled={namespaceAccess.readOnly}
              />
            )}
            {!isAdmin && namespaceAccess.readOnly && (
              <div className="mt-2 flex items-center gap-2 rounded-md border border-blue-100 bg-blue-50 px-3 py-2 text-xs text-blue-700">
                <AlertCircle className="h-3.5 w-3.5" />
                Namespace is controlled by your workspace policy.
              </div>
            )}
          </FormField>

          {/* Description */}
          <FormField label="Description">
            <Input
              type="text"
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              placeholder="Optional description"
            />
          </FormField>

          {/* Values Configuration */}
          <div className="space-y-3">
            <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
              <label className="block text-sm font-medium text-slate-700">
                Configuration Values
              </label>
              <div className="flex gap-1 rounded-md border border-slate-200 bg-slate-50 p-1">
                <ModeButton active={inputMethod === "quick"} onClick={() => setInputMethod("quick")} icon={Sparkles} label="Quick" />
                <ModeButton active={inputMethod === "form"} onClick={() => setInputMethod("form")} icon={FormInput} label="Guided" disabled={!valuesSchema?.properties} />
                <ModeButton active={inputMethod === "yaml"} onClick={() => setInputMethod("yaml")} icon={FileCode} label="YAML" />
              </div>
            </div>

            {loadingSchema ? (
              <LoadingState message="Loading configuration schema..." size="sm" />
            ) : inputMethod === 'quick' ? (
              <div className="rounded-lg border border-blue-100 bg-blue-50/70 p-4">
                <div className="flex items-start gap-3">
                  <Sparkles className="mt-0.5 h-5 w-5 text-blue-600" />
                  <div className="space-y-2 text-sm text-slate-700">
                    <p className="font-medium text-slate-900">
                      Quick launch uses the chart defaults.
                    </p>
                    <p>
                      {isVllmChart
                        ? "For vLLM charts, this keeps the one-click path stable while image/model/resource overrides stay in Guided or YAML."
                        : "Use Guided or YAML only when this release needs explicit overrides."}
                    </p>
                    <div className="flex flex-wrap gap-2 pt-1">
                      <Badge variant="info" size="sm">No values override</Badge>
                      {defaultValuesYaml && (
                        <Badge variant="secondary" size="sm">
                          Chart values.yaml available
                        </Badge>
                      )}
                      {valuesSchema?.properties && (
                        <Badge variant="secondary" size="sm">
                          {schemaDefaultCount} schema defaults detected
                        </Badge>
                      )}
                    </div>
                    {defaultValuesYaml && (
                      <div className="pt-2">
                        <Button
                          type="button"
                          variant="secondary"
                          size="sm"
                          icon={FileCode}
                          onClick={() => {
                            setValuesYaml(defaultValuesYaml);
                            setYamlError(null);
                            setInputMethod("yaml");
                          }}
                        >
                          Load Defaults from values.yaml
                        </Button>
                      </div>
                    )}
                  </div>
                </div>
              </div>
            ) : inputMethod === 'form' && valuesSchema ? (
              <div className="space-y-3">
                {schemaDefaultCount > 0 && (
                  <div className="flex justify-end">
                    <Button
                      type="button"
                      variant="secondary"
                      size="sm"
                      icon={SlidersHorizontal}
                      onClick={() => setValuesForm(schemaDefaults)}
                    >
                      Load Defaults
                    </Button>
                  </div>
                )}
                <div className="border border-slate-200 rounded-lg p-4 max-h-96 overflow-y-auto bg-slate-50">
                  <SchemaFormGenerator
                    schema={valuesSchema}
                    values={valuesForm}
                    onChange={setValuesForm}
                  />
                </div>
              </div>
            ) : (
              <FormField
                help={valuesSchema 
                  ? "Optional: advanced YAML overrides. Invalid YAML is blocked before submit."
                  : "Optional: Chart does not provide a schema. Enter YAML configuration manually."
                }
              >
                <div className="mb-2 flex flex-wrap justify-end gap-2">
                  {defaultValuesYaml && (
                    <Button
                      type="button"
                      variant="secondary"
                      size="sm"
                      icon={FileCode}
                      onClick={() => {
                        setValuesYaml(defaultValuesYaml);
                        setYamlError(null);
                      }}
                    >
                      Load Defaults from values.yaml
                    </Button>
                  )}
                  {!defaultValuesYaml && schemaDefaultCount > 0 && (
                    <Button
                      type="button"
                      variant="secondary"
                      size="sm"
                      icon={SlidersHorizontal}
                      onClick={() => {
                        setValuesYaml(stringifyYaml(schemaDefaults));
                        setYamlError(null);
                      }}
                    >
                      Load Schema Defaults
                    </Button>
                  )}
                  <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={() => {
                      setValuesYaml("");
                      setYamlError(null);
                    }}
                  >
                    Clear
                  </Button>
                </div>
                <Textarea
                  value={valuesYaml}
                  onChange={(e) => {
                    const next = e.target.value;
                    setValuesYaml(next);
                    if (!next.trim()) {
                      setYamlError(null);
                      return;
                    }
                    try {
                      parseValuesYaml(next);
                      setYamlError(null);
                    } catch (err) {
                      setYamlError(err instanceof Error ? err.message : "Invalid YAML");
                    }
                  }}
                  placeholder="# Enter custom values in YAML format&#10;# Example:&#10;# replicaCount: 3&#10;# image:&#10;#   repository: myapp&#10;#   tag: latest"
                  rows={8}
                  className="font-mono text-sm"
                  error={yamlError || undefined}
                />
              </FormField>
            )}
          </div>

          {/* Artifact Info */}
          <div className="p-4 bg-slate-50 border border-slate-200 rounded-lg space-y-2">
            <div className="flex items-center justify-between text-sm">
              <span className="text-slate-500">Repository:</span>
              <span className="text-slate-900 font-mono">{repositoryName}</span>
            </div>
            <div className="flex items-center justify-between text-sm">
              <span className="text-slate-500">Tag:</span>
              <Badge variant="info" size="sm">{tag}</Badge>
            </div>
            <div className="flex items-center justify-between text-sm">
              <span className="text-slate-500">Type:</span>
              <span className="text-slate-900">{artifactType}</span>
            </div>
          </div>
        </form>
    </Modal>
  );
};

const isJsonSchemaObject = (value: unknown): value is JsonSchema =>
  typeof value === "object" && value !== null && !Array.isArray(value);

const ModeButton: React.FC<{
  active: boolean;
  disabled?: boolean;
  icon: React.ComponentType<{ className?: string }>;
  label: string;
  onClick: () => void;
}> = ({ active, disabled = false, icon: Icon, label, onClick }) => (
  <button
    type="button"
    onClick={onClick}
    disabled={disabled}
    className={`flex items-center gap-2 rounded px-3 py-1.5 text-xs font-medium transition ${
      active
        ? "bg-white text-blue-700 shadow-sm ring-1 ring-slate-200"
        : "text-slate-500 hover:text-slate-800 disabled:cursor-not-allowed disabled:opacity-40"
    }`}
  >
    <Icon className="h-3.5 w-3.5" />
    {label}
  </button>
);

const extractJsonSchema = (schemaResponse: unknown): JsonSchema | null => {
  if (schemaResponse == null) {
    return null;
  }

  const tryParse = (value: unknown): unknown => {
    if (typeof value === "string") {
      try {
        return JSON.parse(value);
      } catch {
        return null;
      }
    }
    return value;
  };

  let candidate: unknown = tryParse(schemaResponse);

  if (candidate && typeof candidate === "object" && "schema" in (candidate as Record<string, unknown>)) {
    const inner = (candidate as { schema?: unknown }).schema;
    const normalizedInner = extractJsonSchema(inner);
    if (normalizedInner) {
      return normalizedInner;
    }
  }

  if (isJsonSchemaObject(candidate)) {
    return candidate as JsonSchema;
  }

  return null;
};

const getNamespaceAccess = (cluster?: ClusterWithNamespacePolicy, userNamespace?: string, userRole?: string) => {
  if (userRole === "admin") {
    return {
      allowedNamespaces: [] as string[],
      defaultNamespace: "default",
      readOnly: false,
    };
  }

  const policy = cluster?.namespacePolicy;
  const policyObject = isRecord(policy) ? policy : undefined;
  const allowedNamespaces = uniqueStrings([
    ...(Array.isArray(cluster?.allowedNamespaces) ? cluster.allowedNamespaces : []),
    ...(Array.isArray(policyObject?.allowedNamespaces) ? policyObject.allowedNamespaces : []),
  ]);
  const defaultNamespace =
    firstString(cluster?.defaultNamespace, policyObject?.defaultNamespace, policyObject?.namespace) ??
    allowedNamespaces[0] ??
    userNamespace ??
    "default";
  const policyName = typeof policy === "string" ? policy.toLowerCase() : "";
  const readOnly =
    cluster?.namespaceReadOnly === true ||
    cluster?.namespaceReadonly === true ||
    policyObject?.readOnly === true ||
    policyObject?.readonly === true ||
    ["readonly", "read_only", "allowed_only", "restricted"].includes(policyName) ||
    allowedNamespaces.length > 0;

  return {
    allowedNamespaces,
    defaultNamespace,
    readOnly,
  };
};

const uniqueStrings = (values: unknown[]): string[] =>
  Array.from(
    new Set(
      values.filter((value): value is string => typeof value === "string" && value.trim().length > 0)
    )
  );

const firstString = (...values: unknown[]): string | undefined =>
  values.find((value): value is string => typeof value === "string" && value.trim().length > 0);

const parseValuesYaml = (source: string): Record<string, any> => {
  const parsed = parseYaml(source);
  if (parsed == null) {
    return {};
  }
  if (typeof parsed !== "object" || Array.isArray(parsed)) {
    throw new Error("Values YAML must be an object");
  }
  return parsed as Record<string, any>;
};

const extractSchemaDefaults = (schema: JsonSchema): Record<string, any> => {
  if (schema.default !== undefined && isRecord(schema.default)) {
    return schema.default as Record<string, any>;
  }

  const output: Record<string, any> = {};
  for (const [key, property] of Object.entries(schema.properties ?? {})) {
    const defaultValue = defaultForSchema(property);
    if (defaultValue !== undefined) {
      output[key] = defaultValue;
    }
  }
  return output;
};

const defaultForSchema = (schema: JsonSchema): unknown => {
  if (schema.default !== undefined) {
    return schema.default;
  }

  if (schema.type === "object" && schema.properties) {
    const nested: Record<string, unknown> = {};
    for (const [key, property] of Object.entries(schema.properties)) {
      const defaultValue = defaultForSchema(property);
      if (defaultValue !== undefined) {
        nested[key] = defaultValue;
      }
    }
    return Object.keys(nested).length > 0 ? nested : undefined;
  }

  return undefined;
};

const pruneEmptyValues = (value: Record<string, any>): Record<string, any> => {
  const output: Record<string, any> = {};
  for (const [key, fieldValue] of Object.entries(value)) {
    if (isRecord(fieldValue)) {
      const nested = pruneEmptyValues(fieldValue as Record<string, any>);
      if (Object.keys(nested).length > 0) {
        output[key] = nested;
      }
    } else if (fieldValue !== "" && fieldValue !== undefined && fieldValue !== null) {
      output[key] = fieldValue;
    }
  }
  return output;
};

const countLeafValues = (value: unknown): number => {
  if (!isRecord(value)) {
    return value === undefined ? 0 : 1;
  }
  return Object.values(value).reduce<number>((sum, item) => sum + countLeafValues(item), 0);
};

const isRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === "object" && value !== null && !Array.isArray(value);
