package install

import (
    "fmt"
    "errors"
    "sync"
    // "time"

    log "oc-deploy/log_wrapper"
    "oc-deploy/utils"
    "oc-deploy/tool"
    "oc-deploy/helm"
    "oc-deploy/chart"
    "oc-deploy/kubectl"
)

type InstallClass struct {
    Version string
    Workspace string

    versionFile string
    tools []tool.ToolData
    toolsBin map[string]string
    charts []chart.ChartRepoData
}

func (this *InstallClass) New() (string, error) {

    // Extraction du fichier de version
    log.Log().Debug().Msg("Téléchargement du fichier de version")
    src := fmt.Sprintf("../offline/oc_%s.yml", this.Version)
    dst := fmt.Sprintf("%s/oc.yml", this.Workspace)

    err := utils.CopyFile(src, dst)
    if err != nil {
        return "", err
    }

    // Lecture du fichier de conf
    this.versionFile = dst

    this.tools = tool.FromConfigFile(this.versionFile)

    this.charts = chart.FromConfigFile(this.versionFile)

    return dst, nil
}

func (this *InstallClass) Tools() (error) {

    var mem []tool.ToolClass

    for _, v := range this.tools {

        tool2 := tool.ToolClass{}
        v.Bin = this.Workspace
        err := tool2.New(v)
        if err != nil {
            return err
        }
        mem = append(mem,tool2)
    }

    this.toolsBin = make(map[string]string)

    for _, p := range mem {
        data := p.Obj.Get()
        log.Log().Info().Msg(fmt.Sprintf("  >> Outils    : %s", data.Name))
        err := p.Locate()
        if err != nil {
            log.Log().Info().Msg(fmt.Sprintf("  <<            %s ", err))
            return err
        }
        log.Log().Info().Msg(fmt.Sprintf("  <<             %s ", p.Path))
        version, err1 := p.Version()
        if err1 != nil {
            log.Log().Info().Msg(fmt.Sprintf("  <<             %s ", err1))
            return err1
        }
        log.Log().Info().Msg(fmt.Sprintf("  <<             %s ", version))

        this.toolsBin[data.Name] = p.Path
    }

    return nil
}

func (this *InstallClass) getToolBin(name string) (string, error) {
    for key, value := range this.toolsBin {
        if key == name {
            return value, nil
        }
    }

    return  "", errors.New("Error")
}

func (this *InstallClass) ChartRepo() (error) {

    bin_path, _ := this.getToolBin("helm")

    for _, v := range this.charts {
        log.Log().Info().Msg(fmt.Sprintf("  >> Helm Repo : %s", v.Name))
        repo := helm.HelmRepo{Bin: bin_path,
                              Name: v.Name,
                              Repository: v.Repository,
                              ForceUpdate: true}
        res, err := repo.AddRepository()
        if err != nil {
            log.Log().Info().Msg(fmt.Sprintf("  <<             %s ", err))
            return err
        }
        log.Log().Info().Msg(fmt.Sprintf("  <<             %s ", res))

    }
    return nil
}

func (this *InstallClass) K8s(context string) (error) {
    bin_path, _ := this.getToolBin("kubectl")

    kube := kubectl.KubeContext{Bin: bin_path}

    err := kube.UseContext(context)
    if err != nil {
        log.Log().Info().Msg(fmt.Sprintf("  << Kube      : %s ", err))
        return err
    }

    currentcontext, namespace, server, err := kube.GetContext()
    if err != nil {
        log.Log().Info().Msg(fmt.Sprintf("  << Kube      : %s ", err))
        return err
    }
    log.Log().Info().Msg(fmt.Sprintf("  << Kube      : %s ", currentcontext))

    log.Log().Info().Msg(fmt.Sprintf("  <<           : %s ", namespace))
    log.Log().Info().Msg(fmt.Sprintf("  <<           : %s ", server))

    err = kube.Check()
    if err != nil {
        log.Log().Info().Msg(fmt.Sprintf("  <<           : %s ", err))
        return err
    }
    log.Log().Info().Msg(fmt.Sprintf("  <<           : %s ", "OK"))

    return nil
}

func (this *InstallClass) Charts() (error) {
    helm_bin, _ := this.getToolBin("helm")
    kubectl_bin, _ := this.getToolBin("kubectl")

    var wg sync.WaitGroup

    for _, v := range this.charts {
        for _, v1 := range v.Charts {
            wg.Add(1)

            go func() {
                defer wg.Done()
                this.worker(helm_bin, kubectl_bin, v1)
            } ()
            
        }
    }
    wg.Wait()
    return nil
}

func (this *InstallClass) worker(helm_bin string, kubectl_bin string, chart chart.ChartData) {

    log.Log().Info().Msg(fmt.Sprintf("  << Chart     : %s ", chart.Name))

    helmchart := helm.HelmChart{Bin: helm_bin,
                                Name: chart.Name,
                                Chart: chart.Chart,
                                Version: chart.Version,
                                Workspace: this.Workspace,
                            	Opts: chart.Opts,
                                Values: chart.Values,
                                FileValues: chart.FileValues}

    obj := kubectl.KubeObject{Bin: kubectl_bin,
                              Name: chart.Name}

    res, err := helmchart.Install()
    if err != nil {
        log.Log().Error().Msg(fmt.Sprintf("  >>              %s %s (%s)", helmchart.Name, "KO", err))
        return
    }
    log.Log().Info().Msg(fmt.Sprintf("  >>             %s (%s)", helmchart.Name, res))

    err = obj.Wait()
    if err != nil {
        log.Log().Error().Msg(fmt.Sprintf("  >>             %s %s (%s)", chart.Name, "KO", err))
    } else {
        log.Log().Info().Msg(fmt.Sprintf("  >>             %s %s", chart.Name, "OK"))
    }
}