import { readFileSync, existsSync } from "fs";
import path from "path";
import { mulmoScriptSchema } from "../types/schema.js";
import { getScriptFromPromptTemplate } from "../utils/file.js";
import { currentMulmoScriptVersion } from "../types/const.js";
import { promptTemplates } from "../data/index.js";
import { loadMulmoConfig } from "../utils/mulmo_config.js";
/**
 * Add $mulmocast version if not present
 */
export const addMulmocastVersion = (data) => {
    if (data.$mulmocast) {
        return data;
    }
    return {
        ...data,
        $mulmocast: { version: currentMulmoScriptVersion },
    };
};
const deepMergeKeys = ["speechParams", "imageParams", "movieParams", "audioParams", "slideParams"];
/**
 * Merge base with override (override takes precedence)
 */
export const mergeScripts = (base, override) => {
    const merged = { ...base, ...override };
    deepMergeKeys.forEach((key) => {
        if (base[key] && override[key]) {
            merged[key] = { ...base[key], ...override[key] };
        }
    });
    return merged;
};
/**
 * Check if style specifier is a file path
 */
const isFilePath = (style) => {
    return style.endsWith(".json") || style.includes("/") || style.includes("\\");
};
/**
 * Get style by name from promptTemplates
 */
const getStyleByName = (styleName) => {
    const template = promptTemplates.find((t) => t.filename === styleName);
    return template?.presentationStyle;
};
/**
 * Get style from file path
 */
const getStyleFromFile = (filePath) => {
    const resolvedPath = path.resolve(filePath);
    if (!existsSync(resolvedPath)) {
        return undefined;
    }
    const content = readFileSync(resolvedPath, "utf-8");
    return JSON.parse(content);
};
/**
 * Get style by name or file path
 */
export const getStyle = (style) => {
    return isFilePath(style) ? getStyleFromFile(style) : getStyleByName(style);
};
/**
 * Complete a partial MulmoScript with schema defaults, optional style or template
 *
 * @param data - Partial MulmoScript to complete (highest precedence)
 * @param options - Optional template or style to use as base
 * @param options.templateName - Template name (e.g., "children_book"). Mutually exclusive with styleName.
 * @param options.styleName - Style name or file path. Mutually exclusive with templateName.
 * @returns Zod safe parse result with completed MulmoScript or validation errors
 * @throws Error if both templateName and styleName are specified
 *
 * @example
 * // With template
 * completeScript(data, { templateName: "children_book" })
 *
 * @example
 * // With style
 * completeScript(data, { styleName: "ghibli_comic" })
 *
 * @example
 * // With style from file
 * completeScript(data, { styleName: "./my-style.json" })
 */
export const completeScript = (data, options = {}) => {
    const { templateName, styleName, baseDirPath } = options;
    // template and style are mutually exclusive
    if (templateName && styleName) {
        throw new Error("Cannot specify both templateName and styleName. They are mutually exclusive.");
    }
    // Load mulmo.config.json
    const configResult = baseDirPath ? loadMulmoConfig(baseDirPath) : null;
    // Get base config from template or style
    const getBase = () => {
        if (templateName) {
            return getScriptFromPromptTemplate(templateName);
        }
        if (styleName) {
            return getStyle(styleName);
        }
        return undefined;
    };
    const templateOrStyle = getBase();
    // Merge chain: config.defaults < template/style < input data < config.override
    const defaults = configResult?.defaults;
    const withDefaults = defaults && templateOrStyle ? mergeScripts(defaults, templateOrStyle) : (templateOrStyle ?? defaults);
    const withData = withDefaults ? mergeScripts(withDefaults, data) : data;
    const merged = configResult?.override ? mergeScripts(withData, configResult.override) : withData;
    // Add version if not present
    const withVersion = addMulmocastVersion(merged);
    return mulmoScriptSchema.safeParse(withVersion);
};
/**
 * Check if template exists
 */
export const templateExists = (templateName) => {
    return getScriptFromPromptTemplate(templateName) !== undefined;
};
/**
 * Check if style exists (by name or file path)
 */
export const styleExists = (style) => {
    return getStyle(style) !== undefined;
};
