// SPDX-License-Identifier: Apache-2.0
import sinon from 'sinon';
import { before, beforeEach, afterEach, describe, it } from 'mocha';
import { expect } from 'chai';
import { container } from 'tsyringe-neo';
import { resetForTest } from '../../test-container.js';
import { InjectTokens } from '../../../src/core/dependency-injection/inject-tokens.js';
import { Flags as flags } from '../../../src/commands/flags.js';
import { NamespaceName } from '../../../src/types/namespace/namespace-name.js';
import { Argv } from '../../helpers/argv-wrapper.js';
import { ValueContainer } from '../../../src/core/dependency-injection/value-container.js';
import { K8Client } from '../../../src/integration/kube/k8-client/k8-client.js';
describe('DeploymentCommand unit tests', () => {
    const namespace = NamespaceName.of('solo-e2e');
    const deploymentName = 'deployment';
    const k8FactoryStub = sinon.stub();
    let containerOverrides;
    let realK8Factory;
    let namespacesStub;
    let configMapsStub;
    let k8Stub;
    before(() => {
        realK8Factory = container.resolve(InjectTokens.K8Factory);
    });
    beforeEach(async () => {
        namespacesStub = sinon.stub();
        configMapsStub = sinon.stub();
        k8Stub = {};
        const factoryStubbed = k8FactoryStub;
        factoryStubbed.getK8 = sinon.stub().returns(k8Stub);
        factoryStubbed.default = sinon.stub().returns(k8Stub);
        k8Stub.namespaces = sinon.stub().returns({
            has: namespacesStub,
            list: sinon.stub().resolves([]),
        });
        k8Stub.configMaps = sinon.stub().returns({
            exists: configMapsStub,
            listForAllNamespaces: sinon.stub().resolves([]),
        });
        k8Stub.contexts = sinon.stub().returns({
            readCurrent: sinon
                .stub()
                .returns(new K8Client(undefined, realK8Factory.default().getKubectlExecutablePath()).contexts().readCurrent()),
        });
        k8Stub.clusters = sinon.stub().returns({
            readCurrent: sinon.stub().returns(realK8Factory.default().clusters().readCurrent()),
        });
        k8Stub.leases = sinon.stub().returns({
            read: sinon.stub().rejects(new Error('not found')),
            create: sinon.stub().resolves(),
            delete: sinon.stub().resolves(),
            update: sinon.stub().resolves(),
        });
        containerOverrides = new Map([[InjectTokens.K8Factory, new ValueContainer(InjectTokens.K8Factory, k8FactoryStub)]]);
        resetForTest(undefined, undefined, true, containerOverrides);
    });
    afterEach(() => {
        sinon.restore();
    });
    describe('create() - stale local config detection', () => {
        it('should detect stale local config and clean up when namespace does not exist in cluster', async () => {
            // The test data has a "deployment" entry with cluster-1 → context-1
            // Simulate namespace NOT existing in the cluster (stale local config scenario)
            namespacesStub.resolves(false);
            const deploymentCommand = container.resolve(InjectTokens.DeploymentCommand);
            const localConfig = container.resolve(InjectTokens.LocalConfigRuntimeState);
            const argv = Argv.getDefaultArgv(namespace);
            argv.setArg(flags.deployment, deploymentName);
            argv.setArg(flags.namespace, namespace.name);
            // Should succeed - stale config is cleaned up automatically
            await expect(deploymentCommand.create(argv.build())).to.eventually.be.true;
            // Verify the deployment was re-created (still present in local config)
            await localConfig.load();
            const deployment = localConfig.configuration.deployments.find((d) => d.name === deploymentName);
            expect(deployment).to.not.be.undefined;
            expect(deployment?.namespace).to.equal(namespace.name);
        });
        it('should detect stale local config and clean up when cluster connection fails', async () => {
            // Simulate cluster connection failure (e.g., Kind cluster was deleted)
            k8Stub.namespaces = sinon.stub().returns({
                has: sinon.stub().rejects(new Error('connection refused - cluster no longer exists')),
                list: sinon.stub().rejects(new Error('connection refused')),
            });
            const deploymentCommand = container.resolve(InjectTokens.DeploymentCommand);
            const argv = Argv.getDefaultArgv(namespace);
            argv.setArg(flags.deployment, deploymentName);
            argv.setArg(flags.namespace, namespace.name);
            // Should succeed - stale config is cleaned up when cluster is unreachable
            await expect(deploymentCommand.create(argv.build())).to.eventually.be.true;
        });
        it('should throw "already exists" error when deployment genuinely exists in cluster', async () => {
            // Simulate namespace AND remote config both existing (genuine deployment)
            namespacesStub.resolves(true);
            configMapsStub.resolves(true);
            const deploymentCommand = container.resolve(InjectTokens.DeploymentCommand);
            const argv = Argv.getDefaultArgv(namespace);
            argv.setArg(flags.deployment, deploymentName);
            argv.setArg(flags.namespace, namespace.name);
            // The outer error is "Error creating deployment" wrapping the actual cause
            await expect(deploymentCommand.create(argv.build())).to.be.rejectedWith('Error creating deployment');
        });
        it('should proceed normally when deployment does not exist in local config', async () => {
            const newDeploymentName = 'brand-new-deployment';
            const newNamespace = NamespaceName.of('brand-new-namespace');
            const deploymentCommand = container.resolve(InjectTokens.DeploymentCommand);
            const argv = Argv.getDefaultArgv(newNamespace);
            argv.setArg(flags.deployment, newDeploymentName);
            argv.setArg(flags.namespace, newNamespace.name);
            // Should succeed - new deployment, no conflict
            await expect(deploymentCommand.create(argv.build())).to.eventually.be.true;
        });
    });
    describe('create() - deployment with no cluster refs is treated as stale', () => {
        it('should clean up stale deployment with no cluster refs and create fresh', async () => {
            // Manually add a deployment with no cluster refs to local config
            const localConfig = container.resolve(InjectTokens.LocalConfigRuntimeState);
            await localConfig.load();
            const noClusterDeploymentName = 'no-cluster-deployment';
            const noClusterNamespace = NamespaceName.of('no-cluster-ns');
            const staleDeployment = localConfig.configuration.deployments.addNew();
            staleDeployment.name = noClusterDeploymentName;
            staleDeployment.namespace = noClusterNamespace.name;
            staleDeployment.realm = 0;
            staleDeployment.shard = 0;
            await localConfig.persist();
            const deploymentCommand = container.resolve(InjectTokens.DeploymentCommand);
            const argv = Argv.getDefaultArgv(noClusterNamespace);
            argv.setArg(flags.deployment, noClusterDeploymentName);
            argv.setArg(flags.namespace, noClusterNamespace.name);
            // Should succeed - deployment with no cluster refs is treated as stale
            await expect(deploymentCommand.create(argv.build())).to.eventually.be.true;
        });
    });
});
//# sourceMappingURL=deployment.test.js.map