package compiler

import (
	"bytes"
	"encoding/json"
	"esperframework/compiler/appshell"
	"esperframework/compiler/parser"
	"esperframework/options"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func compileTemplate(opts *options.Options, as appshell.ASTree, td *parser.TemplateData, dg *parser.DepGraph) {
	// Head
	compileHead(opts, &as, td, dg)

	// Body
	compileBody(opts, &as, td, dg)

	// Styles
	compileStyles(opts, &as, td, dg)

	// Scripts
	compileScripts(opts, &as, td, dg)

	rwa, _, _ := strings.Cut(td.MetaData.MetaDataPath, filepath.Base(td.MetaData.MetaDataPath))
	_, rel, _ := strings.Cut(rwa, filepath.Base(opts.AppDir))
	outDir := filepath.Join((*opts).CachePath, rel)

	err := os.MkdirAll(outDir, os.ModePerm)
	if err != nil {
		log.Fatalf("\nFailed to create directory at %s\n%s\n", outDir, err)
	}

	fn, _, _ := parser.SplitLayoutNameAndLayoutDepFromFileName(filepath.Base(td.MetaData.MetaDataPath))
	outPath := filepath.Join((*opts).CachePath, rel, fn+".json")

	file, err := os.Create(outPath)
	if err != nil {
		log.Fatalf("\nFailed to create file for markup at %s\n%s\n", outPath, err)
	}
	defer file.Close()

	// Write file
	buf := new(bytes.Buffer)
	enc := json.NewEncoder(buf)
	enc.SetEscapeHTML(false)

	if err := enc.Encode(&as); err != nil {
		log.Fatalf("\nFailed to encode component JSON\n%s\n", err)
	}

	file.Write(buf.Bytes())
}

func compileHead(opts *options.Options, as *appshell.ASTree, td *parser.TemplateData, dg *parser.DepGraph) {
	templateHeadData := &[]appshell.SlotItemData{}
	templateImportData := &[]appshell.ImportData{}
	headTemplatesSeen := &map[string][]appshell.SlotItemData{}

	aggregateHeadItems(opts, td, dg, templateHeadData, templateImportData, headTemplatesSeen)

	as.HeadSlot.SlotItems = *templateHeadData
	as.Imports = *templateImportData
}

func compileBody(opts *options.Options, as *appshell.ASTree, td *parser.TemplateData, dg *parser.DepGraph) {
	templateBodyData := &[]appshell.SlotItemData{}
	bodyTemplatesSeen := &map[string][]appshell.SlotItemData{}

	aggregateMarkup(opts, td, dg, templateBodyData, bodyTemplatesSeen)

	as.BodySlot.SlotItems = *templateBodyData
}

func compileStyles(opts *options.Options, as *appshell.ASTree, td *parser.TemplateData, dg *parser.DepGraph) {
	templateStylesData := &[]appshell.SlotItemData{}
	stylesTemplatesSeen := &map[string][]appshell.SlotItemData{}

	aggregateStyles(opts, td, dg, templateStylesData, stylesTemplatesSeen)

	as.Styles.SlotItems = *templateStylesData
}

func compileScripts(opts *options.Options, as *appshell.ASTree, td *parser.TemplateData, dg *parser.DepGraph) {
	templateScriptsData := &[]appshell.SlotItemData{}
	scriptsTemplatesSeen := &map[string][]appshell.SlotItemData{}

	aggregateScripts(opts, td, dg, templateScriptsData, scriptsTemplatesSeen)

	as.Scripts.SlotItems = *templateScriptsData
}
