package promote

import (
	"bytes"
	"fmt"
	"gitea-page/internal/config"
	"github.com/rs/zerolog/log"
	"html/template"
	"os"
	"os/exec"
	"time"
)

type Promote struct {
 Config config.GiteaPagesConfig
}

type PromoterData struct {
	Config config.GiteaPagesConfig
	Repo string
	Owner string
	Domain string
	Path string
	SSL struct{
		Active bool
		Cert string
		Key string
	}
}

func (p Promote) PromoteRepo(name, owner string) {
	log.Debug().Str("name", name).Str("owner", owner).Msg("Promote")
	url := p.getDefaultURL(name, owner)
	log.Debug().Str("url", url).Msg("Promote URL")

	content := p.getContent(name, owner, url)
	updated := p.updateContent(url, content)
	if updated {
		p.reloadServer()
	}
	log.Debug().Bool("Need to Reload", updated).Msg("File Created")

}

func (p Promote) reloadServer() {
	reload, err := exec.Command(p.Config.Promoter.Reload.Name, p.Config.Promoter.Reload.Args...).Output()
	if err != nil {
		log.Error().Err(err).Msg("Cant reload Server")
	}
	log.Debug().Str("output", string(reload)).Msg("Reloaded Server")
}

func (p Promote) getSSL(domain, path string) (cert, key string, active bool) {
	if _, err := os.Stat(p.getCertPath(domain, "cer")); err == nil {
		return p.getCertPath(domain, "cer"), p.getCertPath(domain, "key"), true
	}
	return "", "", false
}

func (p Promote) requestSSL(domain, path, name, owner string)  {
	log.Debug().Msg("Try to get SSL Zertifikat")
	output, err := exec.Command("/root/.acme.sh/acme.sh", "--issue", "-d", domain, "-w", path).Output()
	if err != nil {
		log.Error().Str("output", string(output)).Err(err).Msg("Cant issue ssl certificat")
	} else {
		log.Info().Str("output", string(output)).Msg("Got SSL zertifikat")
		p.PromoteRepo(name, owner)
	}
}

func (p Promote) updateContent(domain, content string) bool {
	fileName := fmt.Sprintf("%s/%s.conf", p.Config.Promoter.Path, domain)
	log.Debug().Str("filename", fileName).Msg("Promote to File")
	if _, err := os.Stat(fileName); err == nil {
		c, _ := os.ReadFile(fileName)
		if string(c) == content {
			return false
		}
	}
	os.WriteFile(fileName, []byte(content), 0644)
	return true
}

func (p Promote) getContent(name, owner, domain string) string {
	templateContent, err := os.ReadFile(p.Config.Promoter.Template)
	if err != nil {
		log.Panic().Err(err).Msg("Cant get promoter template")
	}

	var b bytes.Buffer
	templ, err := template.New("").Parse(string(templateContent))
	if err != nil {
		log.Panic().Err(err).Msg("Cant parse Prmoter Template")
	}
	path := fmt.Sprintf("%s/%s/%s", p.Config.RootPath, owner, name)
	promoterData := PromoterData{
		Config: p.Config,
		Repo:   name,
		Owner:  owner,
		Domain: domain,
		Path: path,
	}

	cert, key, active := p.getSSL(domain, path)
	promoterData.SSL.Active = active
	promoterData.SSL.Cert = cert
	promoterData.SSL.Key = key

	err = templ.Execute(&b, promoterData)

	if err != nil {
		log.Panic().Err(err).Msg("Cant parse Promoter Template")
	}

	if active == false {
		go func() {
			time.Sleep(10*time.Second)
			p.requestSSL(domain, path, name, owner)
		}()
	}

	return b.String()
}

func (p Promote) getCertPath(domain, typ string) string {
	var b bytes.Buffer
	templ, err := template.New("").Parse(p.Config.Promoter.CertPath)
	if err != nil {
		log.Panic().Err(err).Msg("Cant parse Cert Path String")
	}
	err = templ.Execute(&b, map[string]interface{}{
		"domain": domain,
		"typ": typ,
	})

	if err != nil {
		log.Panic().Err(err).Msg("Cant get cert path")
	}

	return b.String()
}

func (p Promote) getDefaultURL(name, owner string) string {
	log.Debug().Str("domain", p.Config.Domain).Msg("get Default URL")
	var b bytes.Buffer
	templ, err := template.New("").Parse(p.Config.Domain)
	if err != nil {
		log.Panic().Err(err).Msg("Cant parse Config String")
	}
	err = templ.Execute(&b, map[string]interface{}{
		"repro": name,
		"owner": owner,
	})

	if err != nil {
		log.Panic().Err(err).Msg("Cant get default URL")
	}

	return b.String()
}