import { hasLayerChildren } from '../../../../lib/ui-builder/store/layer-utils.mjs';
import 'react';
import { isVariableReference } from '../../types.mjs';

const renderTemplate = (template, data) => {
  return template.replace(/<%~\s*it\.(\w+)\s*%>/g, (_, key) => {
    return data[key] ?? "";
  });
};
const zodSchemaToTypeString = (schema) => {
  const zodSchema = schema;
  const def = zodSchema?._zod?.def ?? zodSchema?._def;
  if (!def) {
    return "(...args: unknown[]) => unknown";
  }
  const typeName = def.type ?? def.typeName;
  if (typeName === "tuple" || typeName === "ZodTuple") {
    const items = def.items ?? def.rest?.items ?? [];
    if (!items || items.length === 0) {
      return "() => void";
    }
    const paramTypes = items.map((item, index) => {
      const { type: paramType, isOptional } = zodSchemaToParamType(item);
      const optionalMark = isOptional ? "?" : "";
      return `arg${index}${optionalMark}: ${paramType}`;
    }).join(", ");
    return `(${paramTypes}) => void`;
  }
  if (typeName === "object" || typeName === "ZodObject") {
    return "(params: Record<string, unknown>) => void";
  }
  if (typeName === "function" || typeName === "ZodFunction") {
    return "(...args: unknown[]) => unknown";
  }
  return "(...args: unknown[]) => unknown";
};
const zodSchemaToParamType = (schema) => {
  const zodSchema = schema;
  const def = zodSchema?._zod?.def ?? zodSchema?._def;
  if (!def) {
    return { type: "unknown", isOptional: false };
  }
  const typeName = def.type ?? def.typeName;
  if (typeName === "optional" || typeName === "ZodOptional") {
    const innerType = def.innerType ?? def.wrapped;
    return { type: zodSchemaToSingleType(innerType), isOptional: true };
  }
  return { type: zodSchemaToSingleType(schema), isOptional: false };
};
const zodSchemaToSingleType = (schema) => {
  const zodSchema = schema;
  const def = zodSchema?._zod?.def ?? zodSchema?._def;
  if (!def) {
    return "unknown";
  }
  const typeName = def.type ?? def.typeName;
  switch (typeName) {
    case "string":
    case "ZodString":
      return "string";
    case "number":
    case "ZodNumber":
      return "number";
    case "boolean":
    case "ZodBoolean":
      return "boolean";
    case "void":
    case "ZodVoid":
      return "void";
    case "undefined":
    case "ZodUndefined":
      return "undefined";
    case "null":
    case "ZodNull":
      return "null";
    case "any":
    case "ZodAny":
      return "unknown";
    case "unknown":
    case "ZodUnknown":
      return "unknown";
    case "custom":
    case "ZodType":
      return "unknown";
    case "object":
    case "ZodObject":
      return "Record<string, unknown>";
    case "array":
    case "ZodArray":
      return "unknown[]";
    case "optional":
    case "ZodOptional": {
      const innerOptional = def.innerType ?? def.wrapped;
      return `${zodSchemaToSingleType(innerOptional)} | undefined`;
    }
    case "nullable":
    case "ZodNullable": {
      const innerNullable = def.innerType ?? def.wrapped;
      return `${zodSchemaToSingleType(innerNullable)} | null`;
    }
    default:
      return "unknown";
  }
};
const toValidIdentifier = (input) => {
  let identifier = input.trim();
  if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(identifier)) {
    return identifier;
  }
  identifier = identifier.replace(/[^a-zA-Z0-9_$]/g, " ").split(" ").filter((word) => word.length > 0).map((word, index) => {
    if (index === 0) {
      return word.charAt(0).toLowerCase() + word.slice(1);
    }
    return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
  }).join("");
  if (identifier && !/^[a-zA-Z_$]/.test(identifier)) {
    identifier = "_" + identifier;
  }
  if (!identifier || !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(identifier)) {
    identifier = "variable";
  }
  return identifier;
};
const generateVariableIdentifiers = (variables) => {
  const identifierMap = /* @__PURE__ */ new Map();
  const usedIdentifiers = /* @__PURE__ */ new Set();
  variables.forEach((variable) => {
    const baseIdentifier = toValidIdentifier(variable.name);
    let identifier = baseIdentifier;
    let counter = 1;
    while (usedIdentifiers.has(identifier)) {
      identifier = `${baseIdentifier}${counter}`;
      counter++;
    }
    usedIdentifiers.add(identifier);
    identifierMap.set(variable.id, identifier);
  });
  return identifierMap;
};
const generateFunctionIdentifiers = (functionIds, reservedIdentifiers) => {
  const identifierMap = /* @__PURE__ */ new Map();
  const usedIdentifiers = new Set(reservedIdentifiers);
  functionIds.forEach((funcId) => {
    const baseIdentifier = toValidIdentifier(funcId);
    let identifier = baseIdentifier;
    let counter = 1;
    while (usedIdentifiers.has(identifier)) {
      identifier = `${baseIdentifier}${counter}`;
      counter++;
    }
    usedIdentifiers.add(identifier);
    identifierMap.set(funcId, identifier);
  });
  return identifierMap;
};
const collectFunctionMetadataIds = (layer) => {
  const functionIds = /* @__PURE__ */ new Set();
  for (const [key, value] of Object.entries(layer.props)) {
    if (key.startsWith("__function_") && typeof value === "string") {
      functionIds.add(value);
    }
  }
  if (hasLayerChildren(layer)) {
    layer.children.forEach((child) => {
      const childIds = collectFunctionMetadataIds(child);
      childIds.forEach((id) => functionIds.add(id));
    });
  }
  return functionIds;
};
const pageLayerToCode = (page, componentRegistry, variables = [], functionRegistry) => {
  const layers = page.children;
  const { mode, colorTheme, borderRadius, ...restOfProps } = page.props;
  const variableIdentifiers = generateVariableIdentifiers(variables);
  const functionMetadataIds = /* @__PURE__ */ new Set();
  for (const [key, value] of Object.entries(restOfProps)) {
    if (key.startsWith("__function_") && typeof value === "string") {
      functionMetadataIds.add(value);
    }
  }
  if (Array.isArray(layers)) {
    layers.forEach((layer) => {
      const layerFuncIds = collectFunctionMetadataIds(layer);
      layerFuncIds.forEach((id) => functionMetadataIds.add(id));
    });
  }
  const functionVarReservedIds = /* @__PURE__ */ new Set();
  for (const variable of variables) {
    if (variable.type === "function") {
      const identifier = variableIdentifiers.get(variable.id);
      if (identifier) {
        functionVarReservedIds.add(identifier);
      }
    }
  }
  const functionIdentifiers = generateFunctionIdentifiers(functionMetadataIds, functionVarReservedIds);
  for (const variable of variables) {
    if (variable.type === "function" && typeof variable.defaultValue === "string") {
      if (functionMetadataIds.has(variable.defaultValue)) {
        const varIdentifier = variableIdentifiers.get(variable.id);
        if (varIdentifier) {
          functionIdentifiers.set(variable.defaultValue, varIdentifier);
        }
      }
    }
  }
  const pageProps = generatePropsString(restOfProps, variables, variableIdentifiers, functionIdentifiers);
  const imports = /* @__PURE__ */ new Set();
  const collectImports = (layer) => {
    const componentDefinition = componentRegistry[layer.type];
    if (componentDefinition && componentDefinition.from) {
      imports.add(
        createFromString(componentDefinition.from, layer.type, componentDefinition.isFromDefaultExport)
      );
      if (hasLayerChildren(layer)) {
        layer.children.forEach(collectImports);
      }
    }
  };
  let code = "";
  if (Array.isArray(layers)) {
    layers.forEach(collectImports);
    code = layers.map((layer) => generateLayerCode(layer, 1, variables, variableIdentifiers, functionIdentifiers)).join("\n");
  } else {
    code = `{"${layers}"}`;
  }
  const importsString = Array.from(imports).join("\n");
  const variablePropsInterface = generateVariablePropsInterface(
    variables,
    variableIdentifiers,
    functionMetadataIds,
    functionRegistry,
    functionIdentifiers
  );
  const hasRegularVariables = variables.some((v) => v.type !== "function");
  const hasFunctionVariables = variables.some((v) => v.type === "function");
  const hasFunctionMetadata = functionMetadataIds.size > 0;
  const hasFunctions = hasFunctionVariables || hasFunctionMetadata;
  let variablePropsParam = "";
  if (hasRegularVariables && hasFunctions) {
    variablePropsParam = "{ variables, functions }: PageProps";
  } else if (hasRegularVariables) {
    variablePropsParam = "{ variables }: PageProps";
  } else if (hasFunctions) {
    variablePropsParam = "{ functions }: PageProps";
  }
  const finalCode = renderTemplate(reactComponentTemplate, {
    imports: importsString,
    variablePropsInterface,
    variablePropsParam,
    pageProps,
    children: code.split("\n").map((line) => `    ${line}`).join("\n")
  });
  return finalCode;
};
const generateLayerCode = (layer, indent = 0, variables = [], variableIdentifiers, functionIdentifiers) => {
  if (!variableIdentifiers && variables.length > 0) {
    variableIdentifiers = generateVariableIdentifiers(variables);
  }
  const indentation = "  ".repeat(indent);
  let childrenCode = "";
  if (hasLayerChildren(layer) && layer.children.length > 0) {
    childrenCode = layer.children.map((child) => generateLayerCode(child, indent + 1, variables, variableIdentifiers, functionIdentifiers)).join("\n");
  } else if (typeof layer.children === "string") {
    childrenCode = `${indentation}${"  "}{${JSON.stringify(layer.children)}}`;
  }
  if (childrenCode) {
    return `${indentation}<${layer.type}${generatePropsString(
      layer.props,
      variables,
      variableIdentifiers,
      functionIdentifiers
    )}>
${childrenCode}
${indentation}</${layer.type}>`;
  } else {
    return `${indentation}<${layer.type}${generatePropsString(layer.props, variables, variableIdentifiers, functionIdentifiers)} />`;
  }
};
const generatePropsString = (props, variables = [], variableIdentifiers, functionIdentifiers) => {
  if (!variableIdentifiers && variables.length > 0) {
    variableIdentifiers = generateVariableIdentifiers(variables);
  }
  const functionMetadata = /* @__PURE__ */ new Map();
  for (const [key, value] of Object.entries(props)) {
    if (key.startsWith("__function_") && typeof value === "string") {
      const propName = key.replace("__function_", "");
      functionMetadata.set(propName, value);
    }
  }
  const propsArray = Object.entries(props).filter(([key, _]) => !key.startsWith("__function_")).filter(([key, _]) => !functionMetadata.has(key)).filter(([_, value]) => value !== void 0).map(([key, value]) => {
    let propValue;
    if (isVariableReference(value)) {
      const variable = variables.find((v) => v.id === value.__variableRef);
      if (variable && variableIdentifiers) {
        if (variable.type === "function") {
          propValue = `{functions.${variableIdentifiers.get(variable.id)}}`;
        } else {
          propValue = `{variables.${variableIdentifiers.get(variable.id)}}`;
        }
      } else {
        propValue = `{undefined}`;
      }
    } else if (typeof value === "string") {
      propValue = `"${value}"`;
    } else if (typeof value === "number") {
      propValue = `{${value}}`;
    } else {
      propValue = `{${JSON.stringify(value)}}`;
    }
    return `${key}=${propValue}`;
  });
  functionMetadata.forEach((funcId, propName) => {
    const sanitizedId = functionIdentifiers?.get(funcId) ?? toValidIdentifier(funcId);
    propsArray.push(`${propName}={functions.${sanitizedId}}`);
  });
  return propsArray.length > 0 ? ` ${propsArray.join(" ")}` : "";
};
const generateVariablePropsInterface = (variables, variableIdentifiers, functionMetadataIds, functionRegistry, functionIdentifiers) => {
  const hasFunctionMetadata = functionMetadataIds && functionMetadataIds.size > 0;
  if (variables.length === 0 && !hasFunctionMetadata) return "";
  if (!variableIdentifiers) {
    variableIdentifiers = generateVariableIdentifiers(variables);
  }
  const regularVariables = variables.filter((v) => v.type !== "function");
  const functionVariables = variables.filter((v) => v.type === "function");
  const variableTypes = regularVariables.map((variable) => {
    let tsType = "unknown";
    switch (variable.type) {
      case "string":
        tsType = "string";
        break;
      case "number":
        tsType = "number";
        break;
      case "boolean":
        tsType = "boolean";
        break;
    }
    return `    ${variableIdentifiers.get(variable.id)}: ${tsType};`;
  }).join("\n");
  const functionVariableTypes = functionVariables.map((variable) => {
    const funcId = variable.defaultValue;
    const funcDef = typeof funcId === "string" ? functionRegistry?.[funcId] : void 0;
    if (funcDef?.typeSignature) {
      return `    ${variableIdentifiers.get(variable.id)}: ${funcDef.typeSignature};`;
    }
    if (funcDef?.schema) {
      const typeString = zodSchemaToTypeString(funcDef.schema);
      return `    ${variableIdentifiers.get(variable.id)}: ${typeString};`;
    }
    return `    ${variableIdentifiers.get(variable.id)}: (...args: unknown[]) => unknown;`;
  });
  const emittedFunctionIdentifiers = /* @__PURE__ */ new Set();
  for (const variable of functionVariables) {
    const identifier = variableIdentifiers.get(variable.id);
    if (identifier) {
      emittedFunctionIdentifiers.add(identifier);
    }
  }
  const functionMetadataTypes = hasFunctionMetadata ? Array.from(functionMetadataIds).filter((funcId) => {
    const sanitizedId = functionIdentifiers?.get(funcId) ?? toValidIdentifier(funcId);
    return !emittedFunctionIdentifiers.has(sanitizedId);
  }).map((funcId) => {
    const funcDef = functionRegistry?.[funcId];
    const sanitizedId = functionIdentifiers?.get(funcId) ?? toValidIdentifier(funcId);
    if (funcDef?.typeSignature) {
      return `    ${sanitizedId}: ${funcDef.typeSignature};`;
    }
    if (funcDef?.schema) {
      const typeString = zodSchemaToTypeString(funcDef.schema);
      return `    ${sanitizedId}: ${typeString};`;
    }
    return `    ${sanitizedId}: (...args: unknown[]) => unknown;`;
  }) : [];
  const allFunctionTypes = [...functionVariableTypes, ...functionMetadataTypes].join("\n");
  const hasVariables = regularVariables.length > 0;
  const hasFunctions = functionVariables.length > 0 || hasFunctionMetadata;
  if (!hasVariables && !hasFunctions) return "";
  let interfaceContent = "interface PageProps {\n";
  if (hasVariables) {
    interfaceContent += `  variables: {
${variableTypes}
  };
`;
  }
  if (hasFunctions) {
    interfaceContent += `  functions: {
${allFunctionTypes}
  };
`;
  }
  interfaceContent += "}\n\n";
  return interfaceContent;
};
const reactComponentTemplate = `import React from "react";
<%~ it.imports %>

<%~ it.variablePropsInterface %>const Page = (<%~ it.variablePropsParam %>) => {
  return (
    <div<%~ it.pageProps %>>
<%~ it.children %>
    </div>
  );
};

export default Page;
`;
const createFromString = (from, layerType, isFromDefaultExport) => {
  if (isFromDefaultExport) {
    return `import ${layerType} from "${from}";`;
  }
  return `import { ${layerType} } from "${from}";`;
};

export { generateLayerCode, generatePropsString, pageLayerToCode };
