package list

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"text/template"
	"time"

	"github.com/remogatto/cloud"

	karmen_client "gogs.carduccidante.edu.it/karmen/core/client"
	"gogs.carduccidante.edu.it/karmen/core/config"
	"gogs.carduccidante.edu.it/karmen/core/generator/funcmap"
	"gogs.carduccidante.edu.it/karmen/core/orm"
	"gogs.carduccidante.edu.it/karmen/core/util/fileutil"
	"gogs.carduccidante.edu.it/karmen/core/util/libreoffice"
	"gogs.carduccidante.edu.it/karmen/core/util/pandoc"
	tpl_util "gogs.carduccidante.edu.it/karmen/core/util/template"
)

type Data struct {
	Teachers []*orm.Teacher
	Classes  []*orm.Class
}

type ListGenerator struct {
	sync.Mutex
	Config *config.ConfigT

	CloudPath string
	JobID     uint

	Data *Data

	ncClient *cloud.Client
	kaClient *karmen_client.Client
	logger   *JobLogger
}

type JobLogger struct {
	job *orm.Job
}

func NewJobLogger(job *orm.Job) *JobLogger {
	return &JobLogger{job}
}

func (l *JobLogger) Write(p []byte) (n int, err error) {
	os.Stdout.Write(p)
	origLen := len(p)
	if len(p) > 0 && p[len(p)-1] == '\n' {
		p = p[:len(p)-1] // Cut terminating newline
	}
	l.job.Logs = append(l.job.Logs, &orm.Log{Content: string(p)})

	return origLen, nil
}

func (generator *ListGenerator) generate(outputPath string, cloudPath string, funcMap template.FuncMap) {
	filename := filepath.Join(outputPath, "elenco_docenti")

	tplFilename := filepath.Join(cloudPath, "list.tpl.md")
	log.Printf("Downloading %s from the cloud...", tplFilename)

	tplContent, err := generator.ncClient.Download(tplFilename)
	if err != nil {
		log.Println(err)
	}
	log.Println("Download completed.")

	refFilename := filepath.Join(cloudPath, "reference.odt")
	log.Printf("Downloading %s from the cloud...", tplFilename)
	refContent, err := generator.ncClient.Download(refFilename)
	if err != nil {
		log.Println(err)
	}
	log.Println("Download completed.")

	err = ioutil.WriteFile(filepath.Join(outputPath, "reference.odt"), refContent, 0777)
	if err != nil {
		log.Println(err)
	}

	tpl, err := tpl_util.LoadTextTemplateFromString(string(tplContent), funcMap)
	if err != nil {
		log.Println(err)
	}

	f, err := os.Create(filename + ".md")
	if err != nil {
		log.Println(err)
	}
	defer f.Close()

	err = tpl.Execute(f, generator.Data)
	if err != nil {
		log.Println(err)
	}

	odtFilename := fileutil.ReplaceExt(filename, "odt")

	log.Println("Generate", odtFilename)

	if err := pandoc.Convert(filename+".md", odtFilename, "--data-dir", outputPath); err != nil {
		log.Println(outputPath, err)
	}

	if err := libreoffice.Convert(filename+".odt", "pdf", "--outdir", outputPath); err != nil {
		log.Println(err)
	}

}

func NewListGenerator(config *config.ConfigT) *ListGenerator {
	return &ListGenerator{
		Config: config,
		Data:   new(Data),
	}
}

func (generator *ListGenerator) SetJobId(id uint) {
	generator.Lock()
	generator.JobID = id
	generator.Unlock()
}

func (generator *ListGenerator) Run() {
	var err error

	log.Printf("Connecting to the cloud...")
	generator.ncClient, err = cloud.Dial(
		generator.Config.Cloud.Url,
		generator.Config.Cloud.Username,
		generator.Config.Cloud.Password,
	)
	if err != nil {
		log.Println(err)
	}

	log.Printf("Connecting to karmen...")
	generator.kaClient, err = karmen_client.Dial(
		generator.Config.Url,
		generator.Config.Admin.Username,
		generator.Config.Admin.Password,
	)
	if err != nil {
		log.Println(err)
	}

	var job *orm.Job

	if generator.JobID > 0 {
		job, err = generator.kaClient.GetJob(generator.JobID)
		if err != nil {
			log.Println(err)
		}

		generator.logger = NewJobLogger(job)
		log.SetOutput(generator.logger)

		job.Start = time.Now()
		err = generator.kaClient.UpdateJob(job)
		if err != nil {
			log.Println(err)
		}
	}

	generator.Data.Teachers, err = generator.kaClient.GetTeachers()
	if err != nil {
		log.Println(err)
	}

	generator.Data.Classes, err = generator.kaClient.GetClasses()
	if err != nil {
		log.Println(err)
	}

	var (
		outputPath string
		cloudPath  string
	)

	if generator.JobID > 0 {
		outputPath = filepath.Join(generator.Config.Documents.OutputPath, strconv.Itoa(int(job.ID)))
		cloudPath = job.Document.CloudPath
	} else {
		outputPath = "output"
		cloudPath = generator.CloudPath
	}

	if err := os.MkdirAll(outputPath, 0777); err != nil {
		log.Println(err)
	}

	generator.generate(outputPath, cloudPath, funcmap.FuncMap)

	if generator.JobID > 0 {
		job.End = time.Now()

		job.Files = append(job.Files, &orm.File{Path: filepath.Join(outputPath, "elenco_docenti.pdf")})

		err = generator.kaClient.UpdateJob(job)
		if err != nil {
			log.Println(err)
		}
	}

	if !job.Document.KeepArtifacts {
		log.Println("Uploading resulting files to the cloud...")
		src, err := ioutil.ReadFile(filepath.Join(outputPath, "elenco_docenti.pdf"))
		if err != nil {
			log.Println(err)
		}
		err = generator.ncClient.Upload(src, filepath.Join(cloudPath, "elenco_docenti.pdf"))
		if err != nil {
			log.Println(err)
		}
	} else {
		log.Println("Uploading resulting files and artifacts to the cloud...")
		_, err = generator.ncClient.UploadDir(filepath.Join(outputPath, "*"), cloudPath)
		if err != nil {
			log.Println(err)
		}
	}
	log.Println("Files were uploaded to the cloud...")
}
