package list

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

	"github.com/remogatto/cloud"

	karmen_client "gogs.carducci-dante.gov.it/karmen/client"
	"gogs.carducci-dante.gov.it/karmen/core/config"
	"gogs.carducci-dante.gov.it/karmen/core/orm"
	"gogs.carducci-dante.gov.it/karmen/util/fileutil"
	"gogs.carducci-dante.gov.it/karmen/util/libreoffice"
	"gogs.carducci-dante.gov.it/karmen/util/pandoc"
	tpl_util "gogs.carducci-dante.gov.it/karmen/util/template"
)

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

	CloudPath string
	JobID     uint

	ncClient *cloud.Client
	kaClient *karmen_client.Client
}

var funcMap template.FuncMap = template.FuncMap{
	"comma":        comma,
	"groupClasses": groupClasses,
	"abbrev":       abbrev,
	"nbsp":         nbsp,
}

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

	tplContent, err := generator.ncClient.Download(filepath.Join(cloudPath, "list.tpl.md"))
	if err != nil {
		panic(err)
	}

	refContent, err := generator.ncClient.Download(filepath.Join(cloudPath, "reference.odt"))
	if err != nil {
		panic(err)
	}

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

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

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

	err = tpl.Execute(f, teachers)
	if err != nil {
		panic(err)
	}

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

	log.Println("Generate", odtFilename)

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

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

}

func comma(classes []*orm.Class) string {
	var names []string
	for _, c := range classes {
		names = append(names, c.Name)
	}
	return strings.Join(names, ",")
}

func abbrev(name string) string {
	var result string
	splits := strings.Split(name, " ")
	for i := len(splits) - 1; i >= 0; i-- {
		if i == len(splits)-1 {
			result += splits[i] + " "
			continue
		}
		result += strings.ToUpper(string(splits[i][0]) + ".")
	}
	return result
}

func nbsp(num int, text string) string {
	return text + strings.Repeat("&nbsp;", num)
}

func groupClasses(classes []*orm.Class) string {
	var groups []string

	groupByAddresses := make(map[string][]string)
	addressesAbbrev := map[string]string{
		"Linguistico":       "LIN",
		"Classico":          "CLA",
		"Musicale":          "M",
		"Economico sociale": "ES",
		"Scienze umane":     "SU",
	}
	for _, c := range classes {
		groupByAddresses[c.Field] = append(groupByAddresses[c.Field], fmt.Sprintf("%d%s", c.Year, c.Section))
	}

	for address, classes := range groupByAddresses {
		var group string
		if len(classes) > 1 {
			group = fmt.Sprintf("[%s]%s", strings.Join(classes, ","), addressesAbbrev[address])
		} else {
			group = fmt.Sprintf("%s %s", strings.Join(classes, ","), addressesAbbrev[address])
		}
		groups = append(groups, group)
	}
	return strings.Join(groups, ",")
}

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

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

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

	generator.ncClient, err = cloud.Dial(
		generator.Config.Cloud.Url,
		generator.Config.Cloud.Username,
		generator.Config.Cloud.Password,
	)
	if err != nil {
		panic(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 {
			panic(err)
		}

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

	teachers, err := generator.kaClient.GetTeachers()
	if err != nil {
		panic(err)
	}

	var (
		outputPath string
		cloudPath  string
	)

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

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

	generator.generate(outputPath, cloudPath, teachers, funcMap)

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

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

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

	log.Println("Uploading files to the cloud...")
	src, err := ioutil.ReadFile(filepath.Join(outputPath, "elenco_docenti.pdf"))
	if err != nil {
		panic(err)
	}
	err = generator.ncClient.Upload(src, "Documents/elenco_docenti.pdf")
	if err != nil {
		panic(err)
	}
	log.Println("Files were uploaded to the cloud...")
}
