// SPDX-License-Identifier: Apache-2.0
import { expect } from 'chai';
import { after, before, describe, it } from 'mocha';
import each from 'mocha-each';
import fs from 'node:fs';
import { KubectlDependencyManager } from '../../../../../src/core/dependency-managers/index.js';
import { getTestCacheDirectory, getTemporaryDirectory } from '../../../../test-utility.js';
import * as version from '../../../../../version.js';
import { PathEx } from '../../../../../src/business/utils/path-ex.js';
import sinon from 'sinon';
import { platform } from 'node:process';
import { OperatingSystem } from '../../../../../src/business/utils/operating-system.js';
import { container } from 'tsyringe-neo';
import { InjectTokens } from '../../../../../src/core/dependency-injection/inject-tokens.js';
import * as constants from '../../../../../src/core/constants.js';
import { ShellRunner } from '../../../../../src/core/shell-runner.js';
import { resetForTest } from '../../../../test-container.js';
const mockVersionOutputValid = 'Client Version: v1.33.3\nKustomize Version: v5.6.0';
const mockVersionOutputMissingClient = 'Something Else: v1.10.0\nKustomize Version: v5.6.0';
const mockVersionOutputInvalid = 'invalid output';
describe('KubectlDependencyManager', () => {
    const originalPlatform = platform;
    const originalInstallationDirectory = container.resolve(InjectTokens.KubectlInstallationDirectory);
    const temporaryDirectory = PathEx.join(getTemporaryDirectory(), 'bin');
    const localInstallationDirectory = temporaryDirectory;
    let sandbox;
    before(() => {
        resetForTest();
        fs.mkdirSync(temporaryDirectory);
        sandbox = sinon.createSandbox();
    });
    after(() => {
        if (fs.existsSync(temporaryDirectory)) {
            fs.rmSync(temporaryDirectory, { recursive: true });
        }
    });
    afterEach(() => {
        resetForTest();
        sandbox.restore();
    });
    it('should return kubectl version', () => {
        const kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, undefined, undefined);
        expect(kubectlDependencyManager.getRequiredVersion()).to.equal(version.KUBECTL_VERSION);
    });
    it('should be able to check when kubectl not installed', () => {
        const kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, undefined, undefined);
        expect(kubectlDependencyManager.isInstalledLocally()).not.to.be.ok;
    });
    it('should be able to check when kubectl is installed', async () => {
        const kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, undefined, undefined);
        // Create the local executable file for testing
        const localPath = PathEx.join(localInstallationDirectory, constants.KUBECTL);
        fs.writeFileSync(localPath, '');
        expect(kubectlDependencyManager.isInstalledLocally()).to.be.ok;
    });
    describe('when kubectl is installed globally', () => {
        let kubectlDependencyManager;
        let runStub;
        let existsSyncStub;
        let downloaderFetchPackageSpy;
        let packageDownloader;
        let cpSyncStub;
        let chmodSyncStub;
        let rmSyncStub;
        beforeEach(() => {
            packageDownloader = container.resolve(InjectTokens.PackageDownloader);
            downloaderFetchPackageSpy = sandbox.spy(packageDownloader, 'fetchPackage');
            kubectlDependencyManager = new KubectlDependencyManager(undefined, temporaryDirectory, process.arch, undefined);
            kubectlDependencyManager.uninstallLocal();
            runStub = sandbox.stub(kubectlDependencyManager, 'run');
            cpSyncStub = sandbox.stub(fs, 'cpSync').returns();
            chmodSyncStub = sandbox.stub(fs, 'chmodSync').returns();
            existsSyncStub = sandbox.stub(fs, 'existsSync').returns(true);
            rmSyncStub = sandbox.stub(fs, 'rmSync').returns();
        });
        afterEach(() => {
            container.register(InjectTokens.OsPlatform, { useValue: originalPlatform });
            container.register(InjectTokens.KubectlInstallationDirectory, { useValue: originalInstallationDirectory });
            sandbox.restore();
            resetForTest();
        });
        it('should prefer the global installation if it meets the requirements', async () => {
            runStub.withArgs('which kubectl').resolves(['/usr/local/bin/kubectl']);
            runStub.withArgs('"/usr/local/bin/kubectl" version --client').resolves(mockVersionOutputValid.split('\n'));
            runStub
                .withArgs(`"${localInstallationDirectory}/kubectl" version --client`)
                .resolves(mockVersionOutputValid.split('\n'));
            existsSyncStub.withArgs(`${localInstallationDirectory}/kubectl`).returns(false);
            // @ts-expect-error TS2341: Property isInstalledGloballyAndMeetsRequirements is private
            const result = await kubectlDependencyManager.isInstalledGloballyAndMeetsRequirements();
            expect(result).to.be.true;
            expect(await kubectlDependencyManager.install(getTestCacheDirectory())).to.be.true;
            // Should return global path since it meets requirements
            expect(await kubectlDependencyManager.getExecutable()).to.equal(constants.KUBECTL);
        });
        it('should install kubectl locally if the global installation does not meet the requirements', async () => {
            const originalShellRun = ShellRunner.prototype.run;
            sandbox.stub(ShellRunner.prototype, 'run').callsFake(async function (cmd) {
                if (cmd === `which ${constants.KUBECTL}`) {
                    throw new Error('kubectl not found');
                }
                return originalShellRun.call(this, cmd);
            });
            expect(await kubectlDependencyManager.install(getTestCacheDirectory())).to.be.true;
            expect(fs.existsSync(PathEx.join(localInstallationDirectory, constants.KUBECTL))).to.be.ok;
            expect(await kubectlDependencyManager.getExecutable()).to.equal(constants.KUBECTL);
        });
        it('should be able to use local installation on repeated calls without reinstalling given global installation does not meet requirements', async () => {
            // remove the runStub override of the original method
            runStub.restore();
            existsSyncStub.restore();
            cpSyncStub.restore();
            chmodSyncStub.restore();
            rmSyncStub.restore();
            const originalShellRun = ShellRunner.prototype.run;
            sandbox.stub(ShellRunner.prototype, 'run').callsFake(async function (cmd) {
                if (cmd === `which ${constants.KUBECTL}`) {
                    throw new Error('kubectl not found');
                }
                return originalShellRun.call(this, cmd);
            });
            const kubectlInstallationDirectory = getTemporaryDirectory();
            container.register(InjectTokens.KubectlInstallationDirectory, { useValue: kubectlInstallationDirectory });
            kubectlDependencyManager = new KubectlDependencyManager(undefined, undefined, process.arch, undefined);
            const originalInstallationMeetsRequirements = kubectlDependencyManager.installationMeetsRequirements.bind(kubectlDependencyManager);
            sandbox
                .stub(KubectlDependencyManager.prototype, 'installationMeetsRequirements')
                .callsFake(async (executablePath) => {
                if (executablePath === constants.KUBECTL) {
                    return false; // global installation does not meet requirements
                }
                return originalInstallationMeetsRequirements(executablePath);
            });
            expect(await kubectlDependencyManager.install(getTemporaryDirectory())).to.be.true;
            expect(downloaderFetchPackageSpy.calledOnce).to.be.true;
            expect(fs.existsSync(PathEx.join(kubectlInstallationDirectory, constants.KUBECTL))).to.be.ok;
            // Call install again, should not trigger installation since local installation meets requirements
            downloaderFetchPackageSpy.resetHistory();
            expect(downloaderFetchPackageSpy.notCalled).to.be.true;
            expect(await kubectlDependencyManager.install(getTemporaryDirectory())).to.be.true;
            expect(downloaderFetchPackageSpy.notCalled).to.be.true;
        });
    });
    describe('Kubectl Installation Tests', () => {
        afterEach(() => {
            container.register(InjectTokens.OsPlatform, { useValue: originalPlatform });
            sandbox.restore();
        });
        each([
            [OperatingSystem.OS_LINUX, 'x64'],
            [OperatingSystem.OS_LINUX, 'amd64'],
            [OperatingSystem.OS_WIN32, 'amd64'],
        ]).it('should be able to install kubectl base on %s and %s', async (osPlatform, osArch) => {
            container.register(InjectTokens.OsPlatform, { useValue: osPlatform });
            const kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, osArch, undefined);
            if (fs.existsSync(temporaryDirectory)) {
                fs.rmSync(temporaryDirectory, { recursive: true });
            }
            kubectlDependencyManager.uninstallLocal();
            expect(kubectlDependencyManager.isInstalledLocally()).not.to.be.ok;
            sandbox.stub(ShellRunner.prototype, 'run').withArgs(`which ${constants.KUBECTL}`).alwaysReturned(false);
            expect(await kubectlDependencyManager.install(getTestCacheDirectory())).to.be.true;
            expect(kubectlDependencyManager.isInstalledLocally()).to.be.ok;
            fs.rmSync(temporaryDirectory, { recursive: true });
        });
    });
    describe('KubectlDependencyManager system methods', () => {
        let kubectlDependencyManager;
        beforeEach(() => {
            kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, process.arch, undefined);
        });
        afterEach(() => {
            container.register(InjectTokens.OsPlatform, { useValue: originalPlatform });
            container.register(InjectTokens.KubectlInstallationDirectory, { useValue: originalInstallationDirectory });
            sandbox.restore();
            resetForTest();
        });
        it('getVersion should succeed with valid version output', async () => {
            sandbox.stub(kubectlDependencyManager, 'run').resolves(mockVersionOutputValid.split('\n'));
            expect(await kubectlDependencyManager.getVersion('/usr/local/bin/kubectl')).to.equal('1.33.3');
        });
        it('getVersion should handle error if kubectl version fails', async () => {
            sandbox.stub(kubectlDependencyManager, 'run').rejects(new Error('Command failed'));
            try {
                await kubectlDependencyManager.getVersion('/usr/local/bin/kubectl');
                expect.fail('Should have thrown an error');
            }
            catch (error) {
                expect(error).to.be.instanceOf(Error);
                expect(error.message).to.include('Failed to check kubectl version');
            }
        });
        it('getVersion should handle invalid output', async () => {
            sandbox.stub(kubectlDependencyManager, 'run').resolves(mockVersionOutputInvalid.split('\n'));
            try {
                await kubectlDependencyManager.getVersion('/usr/local/bin/kubectl');
                expect.fail('Should have thrown an error');
            }
            catch (error) {
                expect(error).to.be.instanceOf(Error);
                expect(error.message).to.include('Failed to check kubectl version');
            }
        });
        it('getVersion should handle missing client version in output', async () => {
            sandbox.stub(kubectlDependencyManager, 'run').resolves(mockVersionOutputMissingClient.split('\n'));
            try {
                await kubectlDependencyManager.getVersion('/usr/local/bin/kubectl');
                expect.fail('Should have thrown an error');
            }
            catch (error) {
                expect(error).to.be.instanceOf(Error);
                expect(error.message).to.include('Failed to check kubectl version');
            }
        });
        it('processDownloadedPackage should handle platform-specific executable names', async () => {
            container.register(InjectTokens.OsPlatform, { useValue: OperatingSystem.OS_LINUX });
            // First test with non-Windows platform
            let kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, 'amd64', undefined);
            // @ts-expect-error TS2341: Property processDownloadedPackage is private
            const linuxResult = await kubectlDependencyManager.processDownloadedPackage('/tmp/kubectl', '/tmp');
            expect(linuxResult).to.contain('/tmp/kubectl');
            // Now test with Windows platform
            container.register(InjectTokens.OsPlatform, { useValue: OperatingSystem.OS_WIN32 });
            kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, 'amd64', undefined);
            // @ts-expect-error TS2341: Property processDownloadedPackage is private
            const windowsResult = await kubectlDependencyManager.processDownloadedPackage('/tmp/kubectl.exe');
            expect(windowsResult).to.contain('/tmp/kubectl.exe');
        });
        it('getArtifactName should generate correct URL format based on platform/arch', () => {
            container.register(InjectTokens.OsPlatform, { useValue: OperatingSystem.OS_LINUX });
            const kubectlDependencyManager = new KubectlDependencyManager(undefined, localInstallationDirectory, 'amd64', '1.25.0');
            // @ts-expect-error TS2341: Property getArtifactName is private
            const artifactName = kubectlDependencyManager.getArtifactName();
            expect(artifactName).to.include('1.25.0');
            expect(artifactName).to.include(OperatingSystem.OS_LINUX);
            expect(artifactName).to.include('amd64');
        });
    });
});
//# sourceMappingURL=kubectl-dependency-manager.test.js.map