package department

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

	"github.com/remogatto/cloud"

	karmen_client "gogs.carduccidante.edu.it/karmen/core/client"
	"gogs.carduccidante.edu.it/karmen/core/generator"
	"gogs.carduccidante.edu.it/karmen/core/generator/funcmap"
	"gogs.carduccidante.edu.it/karmen/core/generator/logger"
	"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 {
	Departments []*orm.Department
}

type DepartmentGenerator struct {
	sync.Mutex
	Config *generator.Config

	JobID uint

	Data *Data

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

func (generator *DepartmentGenerator) generate(outputPath string, cloudPath string, funcMap template.FuncMap) ([]*orm.File, error) {
	var files []*orm.File

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

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

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

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

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

	for _, department := range generator.Data.Departments {
		filename := filepath.Join(outputPath, fmt.Sprintf("Dipartimento di %s", department.Name))
		f, err := os.Create(filename + ".md")
		if err != nil {
			return nil, err
		}
		defer f.Close()

		err = tpl.Execute(f, department)
		if err != nil {
			return nil, 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 {
			return nil, err
		}

		files = append(files, &orm.File{Path: filepath.Base(filename) + ".pdf"})
	}

	return files, nil

}

func NewDepartmentGenerator(config *generator.Config) *DepartmentGenerator {
	return &DepartmentGenerator{
		Config: config,
		Data:   new(Data),
	}
}

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

func (generator *DepartmentGenerator) 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 = logger.NewJobLogger(job)
		log.SetOutput(generator.logger)

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

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

	var (
		outputPath string
		cloudPath  string
	)

	if job != nil {
		outputPath = filepath.Join(generator.Config.Documents.OutputPath, fmt.Sprintf("%d/%d", job.DocumentID, job.ID))
		cloudPath = job.Document.CloudPath
	} else {
		outputPath = generator.Config.OutputPath
		cloudPath = generator.Config.CloudPath
	}

	log.Println("Output dir", outputPath)

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

	files, err := generator.generate(outputPath, cloudPath, funcmap.FuncMap)
	if err != nil {
		log.Println(err)
	}

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

		job.Files = append(job.Files, files...)

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

	var keepArtifacts bool

	if job != nil {
		keepArtifacts = job.Document.KeepArtifacts
	} else {
		keepArtifacts = generator.Config.KeepArtifacts
	}

	if !keepArtifacts {
		log.Println("Uploading resulting files to the cloud...")
		_, err = generator.ncClient.UploadDir(filepath.Join(outputPath, "*.pdf"), cloudPath)
		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...")
}
