import * as plugins from "./npmci.plugins";
import * as paths from "./npmci.paths";
import * as NpmciEnv from "./npmci.env";
import {bashBare} from "./npmci.bash";

export let build = function(){
    let done = plugins.q.defer();
    readDockerfiles()
        .then(sortDockerfiles)
        .then(mapDockerfiles)
        .then(buildDockerfiles)
        .then(pushDockerfiles)
        .then(() => {
            done.resolve();
        });
    return done.promise;
}

export let readDockerfiles = function(){
    let done = plugins.q.defer();
    let readDockerfilesArray:Dockerfile[] = []
    plugins.gulp.src("./Dockerfile*")
        .pipe(plugins.through2.obj(function(file,enc,cb){
            let myDockerfile = new Dockerfile({
                filePath:file.path,
                read:true
            });
            readDockerfilesArray.push(myDockerfile);
            cb(null,file);
         },function(){
             done.resolve(readDockerfilesArray);
         }));
    return done.promise;
}

export let sortDockerfiles = function(sortableArrayArg:Dockerfile[]){
    let done = plugins.q.defer();
    let sortedArray:Dockerfile[] = [];
    let cleanTagsOriginal = cleanTagsArrayFunction(sortableArrayArg,sortedArray);
    let sorterFunctionCounter:number = 0;
    let sorterFunction = function(){
        sortableArrayArg.forEach((dockerfileArg)=>{
            let cleanTags = cleanTagsArrayFunction(sortableArrayArg,sortedArray);
            if(cleanTags.indexOf(dockerfileArg.baseImage) == -1 && sortedArray.indexOf(dockerfileArg) == -1){
                sortedArray.push(dockerfileArg);
            };
            if(cleanTagsOriginal.indexOf(dockerfileArg.baseImage) != -1){
                dockerfileArg.localBaseImageDependent = true;
            };
        });
        if(sortableArrayArg.length == sortedArray.length){
            done.resolve(sortedArray);
        } else if (sorterFunctionCounter < 10) {
            sorterFunctionCounter++;
            sorterFunction();
        };
    }
    sorterFunction();
    return done.promise;
};

export let mapDockerfiles = function(sortedArray:Dockerfile[]){
    let done = plugins.q.defer();
    sortedArray.forEach((dockerfileArg) => {
        if(dockerfileArg.localBaseImageDependent){
            sortedArray.forEach((dockfile2:Dockerfile) => {
                if(dockfile2.cleanTag == dockerfileArg.baseImage){
                    dockerfileArg.localBaseDockerfile = dockfile2;
                }
            })
        };
    });
    done.resolve(sortedArray);
    return done.promise;
}

export let buildDockerfiles = (sortedArrayArg:Dockerfile[]) => {
    let done = plugins.q.defer();
    sortedArrayArg.forEach(function(dockerfileArg){
        dockerfileArg.build();
    })
    done.resolve(sortedArrayArg);
    return done.promise;
}

export let pushDockerfiles = function(sortedArrayArg:Dockerfile[]){
    let done = plugins.q.defer();
    sortedArrayArg.forEach(function(dockerfileArg){
        dockerfileArg.push(NpmciEnv.buildStage);
    });
    done.resolve(sortedArrayArg);
    return done.promise;
}

export let pullDockerfileImages = (sortableArrayArg:Dockerfile[],registryArg = "registry.gitlab.com") => {
    let done = plugins.q.defer();
    sortableArrayArg.forEach((dockerfileArg) => {
        dockerfileArg.pull(registryArg);
    });
    done.resolve(sortableArrayArg);
    return done.promise;
}

export let testDockerfiles = (sortedArrayArg:Dockerfile[]) => {
    let done = plugins.q.defer();
    sortedArrayArg.forEach(function(dockerfileArg){
        dockerfileArg.test();
    });
    done.resolve(sortedArrayArg);
    return done.promise;
};

export class Dockerfile {
    filePath:string;
    repo:string;
    version:string;
    cleanTag:string;
    buildTag:string;
    testTag:string;
    releaseTag:string;
    containerName:string
    content:string;
    baseImage:string;
    localBaseImageDependent:boolean;
    localBaseDockerfile:Dockerfile;
    constructor(options:{filePath?:string,fileContents?:string|Buffer,read?:boolean}){
        this.filePath = options.filePath;
        this.repo = NpmciEnv.repo.user + "/" + NpmciEnv.repo.repo;
        this.version = dockerFileVersion(plugins.path.parse(options.filePath).base);
        this.cleanTag = this.repo + ":" + this.version;
        this.buildTag = this.cleanTag;
        this.testTag = dockerTag("registry.gitlab.com",this.repo,this.version,"test");
        this.releaseTag = dockerTag(NpmciEnv.dockerRegistry,this.repo,this.version);
        this.containerName = "dockerfile-" + this.version;
        if(options.filePath && options.read){
            this.content = plugins.smartfile.local.toStringSync(plugins.path.resolve(options.filePath));
        };
        this.baseImage = dockerBaseImage(this.content);
        this.localBaseImageDependent = false;
    };
    build(){
        let done = plugins.q.defer();
        plugins.beautylog.info("now building Dockerfile for " + this.cleanTag);
        bashBare("docker build -t " + this.buildTag + " -f " + this.filePath + " .");
        NpmciEnv.dockerFilesBuilt.push(this);
        done.resolve();
        return done.promise;
    };
    push(stageArg){
        let done = plugins.q.defer();
        let pushTag;
        switch (stageArg){
            case "release":
                pushTag = this.releaseTag;
                break;
            case "test":
            default:
                pushTag = this.testTag;
                break;
        }
        bashBare("docker tag " + this.buildTag + " " + pushTag);
        bashBare("docker push " + pushTag);
        done.resolve();
        return done.promise;
    }
    pull(registryArg:string){
        let pullTag = this.testTag;
        bashBare("docker pull " + pullTag);
        bashBare("docker tag " + pullTag + " " + this.buildTag);
    };
    test(){
        let testFile:string = plugins.path.join(paths.NpmciTestDir,"test_" + this.version + ".sh");
        let testFileExists:boolean = plugins.smartfile.checks.fileExistsSync(testFile);
        if(testFileExists){
            bashBare("docker run --name npmci_test_container " + this.buildTag + " mkdir /npmci_test");
            bashBare("docker cp " + testFile + " npmci_test_container:/npmci_test/test.sh");
            bashBare("docker commit npmci_test_container npmci_test_image");
            bashBare("docker run npmci_test_image sh /npmci_test/test.sh");
            bashBare("docker rm npmci_test_container");
            bashBare("docker rmi --force npmci_test_image");
        } else {
            plugins.beautylog.warn("skipping tests for " + this.cleanTag + " because no testfile was found!");
        }
    };
    getId(){
        let containerId = bashBare("docker inspect --type=image --format=\"{{.Id}}\" " + this.buildTag);
        return containerId;
    };
}

export let dockerFileVersion = function(dockerfileNameArg:string):string{
    let versionString:string;
    let versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/;
    let regexResultArray = versionRegex.exec(dockerfileNameArg);
    if(regexResultArray && regexResultArray.length == 2){
        versionString = regexResultArray[1];        
    } else {
        versionString = "latest";
    }
    return versionString;
}

export let dockerBaseImage = function(dockerfileContentArg:string){
    let baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/
    let regexResultArray = baseImageRegex.exec(dockerfileContentArg)
    return regexResultArray[1];
}

export let dockerTag = function(registryArg:string,repoArg:string,versionArg:string,suffixArg?:string):string{
    let tagString:string;
    let registry = registryArg;
    let repo = repoArg;
    let version = versionArg;
    if(suffixArg){
        version = versionArg + "_" + suffixArg;
    };
    tagString = registry + "/" + repo + ":" + version;
    return tagString;
};

export let cleanTagsArrayFunction = function(dockerfileArrayArg:Dockerfile[],trackingArrayArg:Dockerfile[]):string[]{
    let cleanTagsArray:string[] = [];
    dockerfileArrayArg.forEach(function(dockerfileArg){
        if(trackingArrayArg.indexOf(dockerfileArg) == -1){
            cleanTagsArray.push(dockerfileArg.cleanTag);
        }
    });
    return cleanTagsArray;
}