// SPDX-License-Identifier: Apache-2.0
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var ClusterTaskManager_1;
import { inject, injectable } from 'tsyringe-neo';
import { ShellRunner } from './shell-runner.js';
import { InjectTokens } from './dependency-injection/inject-tokens.js';
import { BrewPackageManager } from './package-managers/brew-package-manager.js';
import { OsPackageManager } from './package-managers/os-package-manager.js';
import { patchInject } from './dependency-injection/container-helper.js';
import { PodmanMode } from '../types/index.js';
import { SoloError } from './errors/solo-error.js';
import * as constants from './constants.js';
import { getTemporaryDirectory } from './helpers.js';
import fs from 'node:fs';
import * as yaml from 'yaml';
import path from 'node:path';
import { KindDependencyManager, PodmanDependencyManager } from './dependency-managers/index.js';
import { MissingActiveContextError } from '../integration/kube/errors/missing-active-context-error.js';
import { MissingActiveClusterError } from '../integration/kube/errors/missing-active-cluster-error.js';
let ClusterTaskManager = ClusterTaskManager_1 = class ClusterTaskManager extends ShellRunner {
    brewPackageManager;
    osPackageManager;
    kindBuilder;
    podmanDependencyManager;
    kindDependencyManager;
    podmanInstallationDirectory;
    k8Factory;
    depManager;
    constructor(brewPackageManager, osPackageManager, kindBuilder, podmanDependencyManager, kindDependencyManager, podmanInstallationDirectory, k8Factory, depManager) {
        super();
        this.brewPackageManager = brewPackageManager;
        this.osPackageManager = osPackageManager;
        this.kindBuilder = kindBuilder;
        this.podmanDependencyManager = podmanDependencyManager;
        this.kindDependencyManager = kindDependencyManager;
        this.podmanInstallationDirectory = podmanInstallationDirectory;
        this.k8Factory = k8Factory;
        this.depManager = depManager;
        this.brewPackageManager = patchInject(brewPackageManager, InjectTokens.BrewPackageManager, ClusterTaskManager_1.name);
        this.osPackageManager = patchInject(osPackageManager, InjectTokens.OsPackageManager, ClusterTaskManager_1.name);
        this.kindBuilder = patchInject(kindBuilder, InjectTokens.KindBuilder, ClusterTaskManager_1.name);
        this.podmanDependencyManager = patchInject(podmanDependencyManager, InjectTokens.KindBuilder, ClusterTaskManager_1.name);
        this.kindDependencyManager = patchInject(kindDependencyManager, InjectTokens.KindBuilder, ClusterTaskManager_1.name);
        this.podmanInstallationDirectory = patchInject(podmanInstallationDirectory, InjectTokens.PodmanInstallationDir, ClusterTaskManager_1.name);
        this.k8Factory = patchInject(k8Factory, InjectTokens.K8Factory, ClusterTaskManager_1.name);
        this.depManager = patchInject(depManager, InjectTokens.DependencyManager, ClusterTaskManager_1.name);
    }
    sudoCallbacks(task) {
        const originalTitle = task.title;
        const onSudoRequested = (message) => {
            task.title = message;
        };
        const onSudoGranted = (message) => {
            task.title = originalTitle;
        };
        return { onSudoGranted, onSudoRequested };
    }
    rootfullInstallTasks(parentTask) {
        return [
            {
                title: 'Install git, iptables...',
                task: async (_, subTask) => {
                    try {
                        await this.run('git version');
                    }
                    catch {
                        this.logger.info('Git not found, installing git...');
                        const { onSudoGranted, onSudoRequested } = this.sudoCallbacks(parentTask);
                        const osPackageManager = this.osPackageManager.getPackageManager();
                        osPackageManager.setOnSudoGranted(onSudoGranted);
                        osPackageManager.setOnSudoRequested(onSudoRequested);
                        await osPackageManager.update();
                        await osPackageManager.installPackages(['git', 'iptables']);
                    }
                },
            },
            {
                title: 'Install brew...',
                task: async (_, subTask) => {
                    const brewInstalled = await this.brewPackageManager.isAvailable();
                    if (!brewInstalled) {
                        this.logger.info('Homebrew not found, installing Homebrew...');
                        if (!(await this.brewPackageManager.install())) {
                            throw new SoloError('Failed to install Homebrew');
                        }
                    }
                },
            },
            {
                title: 'Install podman...',
                task: async (_, subTask) => {
                    try {
                        const podmanVersion = await this.run('podman --version');
                        this.logger.info(`Podman already installed: ${podmanVersion}`);
                    }
                    catch {
                        this.logger.info('Podman not found, installing Podman...');
                        await this.brewPackageManager.installPackages(['podman']);
                        const brewBin = await this.run('which podman');
                        process.env.PATH = `${process.env.PATH}:${brewBin.join('').replace('/podman', '')}`;
                    }
                },
            },
            {
                title: 'Creating local cluster...',
                task: async (context_, task) => {
                    const whichPodman = await this.run('which podman');
                    const podmanPath = whichPodman.join('').replace('/podman', '');
                    const { onSudoGranted, onSudoRequested } = this.sudoCallbacks(task);
                    await this.sudoRun(onSudoRequested, onSudoGranted, `KIND_EXPERIMENTAL_PROVIDER=podman PATH="$PATH:${podmanPath}" ${constants.SOLO_HOME_DIR}/bin/kind create cluster`);
                    // Merge kubeconfig data from root user into normal user's kubeconfig
                    const user = await this.run('whoami');
                    const temporaryDirectory = getTemporaryDirectory();
                    await this.sudoRun(onSudoRequested, onSudoGranted, `cp /root/.kube/config ${temporaryDirectory}/kube-config-root`);
                    await this.sudoRun(onSudoRequested, onSudoGranted, `chown ${user} ${temporaryDirectory}/kube-config-root`);
                    await this.sudoRun(onSudoRequested, onSudoGranted, `chmod 755 ${temporaryDirectory}/kube-config-root`);
                    const rootYamlData = fs.readFileSync(`${temporaryDirectory}/kube-config-root`, 'utf8');
                    const rootConfig = yaml.parse(rootYamlData);
                    let userConfig;
                    const clusterName = 'kind-kind';
                    try {
                        const userYamlData = fs.readFileSync(`/home/${user}/.kube/config`, 'utf8');
                        userConfig = yaml.parse(userYamlData);
                        if (!userConfig.clusters) {
                            userConfig.clusters = [];
                        }
                        userConfig.clusters.push(rootConfig.clusters.find(c => c.name === clusterName));
                        if (!userConfig.contexts) {
                            userConfig.contexts = [];
                        }
                        userConfig.contexts.push(rootConfig.contexts.find(c => c.name === clusterName));
                        if (!userConfig.users) {
                            userConfig.users = [];
                        }
                        userConfig.users.push(rootConfig.users.find(c => c.name === clusterName));
                        userConfig['current-context'] = rootConfig['current-context'];
                    }
                    catch (error) {
                        if (error.code === 'ENOENT') {
                            const kubeConfigDirectory = `/home/${user}/.kube/`;
                            if (!fs.existsSync(kubeConfigDirectory)) {
                                fs.mkdirSync(kubeConfigDirectory, { recursive: true });
                            }
                            userConfig = rootConfig;
                            userConfig.clusters = userConfig.clusters.filter(c => c.name === clusterName);
                            userConfig.contexts = userConfig.contexts.filter(c => c.name === clusterName);
                            userConfig.users = userConfig.users.filter(c => c.name === clusterName);
                        }
                        else {
                            throw error;
                        }
                    }
                    fs.writeFileSync(`/home/${user}/.kube/config`, yaml.stringify(userConfig), 'utf8');
                    fs.rmSync(`${temporaryDirectory}/kube-config-root`);
                },
            },
        ];
    }
    async installationTasks(parentTask) {
        const skipPodmanTasks = !(await this.podmanDependencyManager.shouldInstall());
        if (this.podmanDependencyManager.mode === PodmanMode.ROOTFUL) {
            {
                return skipPodmanTasks ? [this.defaultCreateClusterTask(parentTask)] : this.rootfullInstallTasks(parentTask);
            }
        }
        else if (this.podmanDependencyManager.mode === PodmanMode.VIRTUAL_MACHINE) {
            {
                return [
                    {
                        title: 'Create Podman machine...',
                        task: async () => {
                            await this.podmanDependencyManager.setupConfig();
                            const podmanExecutable = await this.podmanDependencyManager.getExecutablePath();
                            await this.run(`${podmanExecutable} machine init --memory=16384`); // 16GB
                            await this.run(`${podmanExecutable} machine start`);
                        },
                        skip: () => skipPodmanTasks,
                    },
                    {
                        title: 'Configure kind to use podman...',
                        task: async () => {
                            process.env.PATH = `${this.podmanInstallationDirectory}${path.delimiter}${process.env.PATH}`;
                            process.env.KIND_EXPERIMENTAL_PROVIDER = 'podman';
                        },
                        skip: () => skipPodmanTasks,
                    },
                    this.defaultCreateClusterTask(parentTask),
                ];
            }
        }
        return [];
    }
    defaultCreateClusterTask(parentTask) {
        return {
            title: 'Creating local cluster...',
            task: async (context_) => {
                const kindExecutable = await this.kindDependencyManager.getExecutablePath();
                const kindClient = await this.kindBuilder.executable(kindExecutable).build();
                const clusterResponse = await kindClient.createCluster(constants.DEFAULT_CLUSTER);
                parentTask.title = `Created local cluster '${clusterResponse.name}'; connect with context '${clusterResponse.context}'`;
            },
        };
    }
    setupLocalClusterTasks() {
        const self = this;
        return [
            {
                title: 'Install Kind',
                task: async (_, task) => {
                    const podmanDependency = this.podmanDependencyManager;
                    const shouldInstallPodman = await podmanDependency.shouldInstall();
                    const podmanDependencies = shouldInstallPodman && podmanDependency.mode === PodmanMode.VIRTUAL_MACHINE
                        ? [constants.PODMAN, constants.VFKIT, constants.GVPROXY]
                        : [];
                    const deps = [...podmanDependencies, constants.KIND];
                    const subTasks = self.depManager.taskCheckDependencies(deps);
                    // set up the sub-tasks
                    return task.newListr(subTasks, {
                        concurrent: true,
                        rendererOptions: {
                            collapseSubtasks: false,
                        },
                    });
                },
                skip: this.skipKindSetup.bind(this),
            },
            {
                title: 'Create default cluster',
                task: async (_, task) => {
                    const subTasks = await this.installationTasks(task);
                    return task.newListr(subTasks, {
                        concurrent: false, // should not use concurrent as cluster creation may be called before dependencies are finished installing
                        rendererOptions: {
                            collapseSubtasks: false,
                        },
                    });
                },
                skip: this.skipKindSetup.bind(this),
            },
        ];
    }
    async skipKindSetup() {
        try {
            const k8 = this.k8Factory.default();
            const contextName = k8.contexts().readCurrent();
            if (!contextName) {
                return false;
            }
            // Try to verify the cluster is actually accessible by making a simple API call
            try {
                await k8.namespaces().list();
                return true;
            }
            catch {
                // If we can't connect to the cluster, don't skip cluster creation
                // This handles cases where contexts exist but clusters are not running
                return false;
            }
        }
        catch (error) {
            return !(error instanceof MissingActiveContextError || error instanceof MissingActiveClusterError);
        }
    }
};
ClusterTaskManager = ClusterTaskManager_1 = __decorate([
    injectable(),
    __param(0, inject(InjectTokens.BrewPackageManager)),
    __param(1, inject(InjectTokens.OsPackageManager)),
    __param(2, inject(InjectTokens.KindBuilder)),
    __param(3, inject(InjectTokens.PodmanDependencyManager)),
    __param(4, inject(InjectTokens.KindDependencyManager)),
    __param(5, inject(InjectTokens.PodmanInstallationDir)),
    __param(6, inject(InjectTokens.K8Factory)),
    __param(7, inject(InjectTokens.DependencyManager)),
    __metadata("design:paramtypes", [BrewPackageManager,
        OsPackageManager, Function, PodmanDependencyManager,
        KindDependencyManager, String, Object, Function])
], ClusterTaskManager);
export { ClusterTaskManager };
//# sourceMappingURL=cluster-task-manager.js.map