// deno-lint-ignore-file no-explicit-any
import { requireLogin } from "../../core/auth.js";
import { resolveWorkspace, validatePath } from "../../core/context.js";
import { colors, Command, log, SEP, Table, windmillUtils, yamlParseFile, } from "../../../deps.js";
import * as wmill from "../../../gen/services.gen.js";
import { isSuperset } from "../../types.js";
import { readInlinePathSync } from "../../utils/utils.js";
import devCommand from "./dev.js";
import lintCommand from "./lint.js";
import { isVersionsGeq1585 } from "../sync/global.js";
const alreadySynced = [];
function respecializeFields(fields) {
    Object.entries(fields).forEach(([k, v]) => {
        if (typeof v == "object") {
            if (v.value !== undefined) {
                fields[k] = { value: v.value, type: "static" };
            }
            else if (v.expr !== undefined) {
                fields[k] = {
                    expr: v.expr,
                    allowUserResources: v.allowUserResources,
                    type: "javascript",
                };
            }
        }
    });
}
export function repopulateFields(runnables) {
    Object.values(runnables).forEach((v) => {
        if (typeof v == "object") {
            if (v.fields !== undefined) {
                respecializeFields(v.fields);
            }
        }
    });
}
export function replaceInlineScripts(rec, localPath, addType) {
    if (!rec) {
        return;
    }
    if (typeof rec == "object") {
        return Object.entries(rec).flatMap(([k, v]) => {
            if (k == "runType") {
                if (addType) {
                    if (isVersionsGeq1585()) {
                        rec["type"] = "path";
                    }
                    else {
                        rec["type"] = "runnableByPath";
                    }
                }
            }
            else if (k == "inlineScript" && typeof v == "object") {
                if (addType) {
                    if (isVersionsGeq1585()) {
                        rec["type"] = "inline";
                    }
                    else {
                        rec["type"] = "runnableByName";
                    }
                }
                const o = v;
                if (o["content"] && o["content"].startsWith("!inline")) {
                    const basePath = localPath + o["content"].split(" ")[1];
                    o["content"] = readInlinePathSync(basePath);
                }
                if (o["lock"] && o["lock"].startsWith("!inline")) {
                    const basePath = localPath + o["lock"].split(" ")[1];
                    o["lock"] = readInlinePathSync(basePath);
                }
            }
            else {
                replaceInlineScripts(v, localPath, addType);
            }
        });
    }
    return [];
}
export function isExecutionModeAnonymous(app) {
    return app?.["policy"]?.["execution_mode"] == "anonymous";
}
export async function pushApp(workspace, remotePath, localPath, message) {
    if (alreadySynced.includes(localPath)) {
        return;
    }
    alreadySynced.push(localPath);
    remotePath = remotePath.replaceAll(SEP, "/");
    let app = undefined;
    // deleting old app if it exists in raw mode
    try {
        app = await wmill.getAppByPath({
            workspace,
            path: remotePath,
        });
    }
    catch {
        //ignore
    }
    if (isExecutionModeAnonymous(app)) {
        app.public = true;
    }
    // console.log(app);
    if (app) {
        app.policy = undefined;
    }
    if (!localPath.endsWith(SEP)) {
        localPath += SEP;
    }
    const path = localPath + "app.yaml";
    const localApp = (await yamlParseFile(path));
    replaceInlineScripts(localApp.value, localPath, true);
    await generatingPolicy(localApp, remotePath, localApp?.["public"] ??
        localApp?.["policy"]?.["execution_mode"] == "anonymous");
    if (app) {
        if (isSuperset(localApp, app)) {
            log.info(colors.green(`App ${remotePath} is up to date`));
            return;
        }
        log.info(colors.bold.yellow(`Updating app ${remotePath}...`));
        await wmill.updateApp({
            workspace,
            path: remotePath,
            requestBody: {
                deployment_message: message,
                ...localApp,
            },
        });
    }
    else {
        log.info(colors.yellow.bold("Creating new app..."));
        await wmill.createApp({
            workspace,
            requestBody: {
                path: remotePath,
                deployment_message: message,
                ...localApp,
            },
        });
    }
}
export async function generatingPolicy(app, path, publicApp) {
    log.info(colors.gray(`Generating fresh policy for app ${path}...`));
    try {
        app.policy = await windmillUtils.updatePolicy(app.value, undefined);
        app.policy.execution_mode = publicApp ? "anonymous" : "publisher";
    }
    catch (e) {
        log.error(colors.red(`Error generating policy for app ${path}: ${e}`));
        throw e;
    }
}
async function list(opts) {
    const workspace = await resolveWorkspace(opts);
    await requireLogin(opts);
    let page = 0;
    const perPage = 10;
    const total = [];
    while (true) {
        const res = await wmill.listApps({
            workspace: workspace.workspaceId,
            page,
            perPage,
            includeDraftOnly: opts.includeDraftOnly ?? false,
        });
        page += 1;
        total.push(...res);
        if (res.length < perPage) {
            break;
        }
    }
    new Table()
        .header(["path", "summary"])
        .padding(2)
        .border(true)
        .body(total.map((x) => [x.path, x.summary]))
        .render();
}
async function push(opts, filePath, remotePath) {
    if (!validatePath(remotePath)) {
        return;
    }
    const workspace = await resolveWorkspace(opts);
    await requireLogin(opts);
    await pushApp(workspace.workspaceId, remotePath, filePath);
    log.info(colors.bold.underline.green("App pushed"));
}
const command = new Command()
    .description("app related commands")
    .action(list)
    .command("push", "push a local app ")
    .arguments("<file_path:string> <remote_path:string>")
    .action(push)
    .command("dev", devCommand)
    .command("lint", lintCommand)
    .command("generate-locks", "re-generate the lockfiles for app runnables inline scripts that have changed")
    .arguments("[app_folder:string]")
    .option("--yes", "Skip confirmation prompt")
    .option("--dry-run", "Perform a dry run without making changes")
    .option("--default-ts <runtime:string>", "Default TypeScript runtime (bun or deno)")
    .action(async (opts, appFolder) => {
    const { generateLocksCommand } = await import("./app_metadata.js");
    await generateLocksCommand(opts, appFolder);
});
export default command;
