package compiler

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

func aggregateStyles(opts *options.Options, td *parser.TemplateData, dg *parser.DepGraph, templateStylesData *[]appshell.SlotItemData, templatesSeen *map[string][]appshell.SlotItemData) {
	if _, ok := (*templatesSeen)[td.MetaData.MetaDataPath]; ok {
		return
	}

	for _, path := range (*dg)[td.MetaData.MetaDataPath].LayoutPaths {
		if _, ok := (*templatesSeen)[path]; ok {
			continue
		}

		templateData := getTemplateData(opts, path)
		aggregateStyleDeps(opts, &templateData, dg, templateStylesData, templatesSeen)
		appendInternalStyles(&templateData, templateStylesData, parser.TemplateKind[parser.Layout])

		(*templatesSeen)[templateData.MetaData.MetaDataPath] = *templateStylesData
	}

	aggregateStyleDeps(opts, td, dg, templateStylesData, templatesSeen)
	appendInternalStyles(td, templateStylesData, parser.TemplateKind[parser.Layout])

	(*templatesSeen)[td.MetaData.MetaDataPath] = *templateStylesData
}

func appendInternalStyles(td *parser.TemplateData, templateStylesData *[]appshell.SlotItemData, loc string) {
	item := appshell.SlotItemData{}
	item.Path = td.MetaData.MetaDataPath
	item.Typ = loc

	if len(item.Boundaries) == 0 {
		item.Boundaries = make([]string, 2)
	}

	// hash := "WF" + utils.ComputeHash(2, 4)

	// if len(hi.Attrs) == 0 {
	// 	(*hi).Attrs = map[string]string{}
	// } else {
	// 	for key := range (*hi).Attrs {
	// 		if key[:2] == "WF" {
	// 			hash = key
	// 		}
	// 	}
	// }

	idx := indexOf(*templateStylesData, "Path", td.MetaData.MetaDataPath)

	// Handle first item
	if idx == -1 {
		// item.Boundaries[0] = "body " + hash
		// hi.Attrs[hash] = ""

		item.HTML.Open = utils.TruncateStringWhitespace(td.Styles.Data)

		isWhiteSpace := true

		for _, ch := range item.HTML.Open {
			if !unicode.IsSpace(rune(ch)) {
				isWhiteSpace = false
			}
		}

		if isWhiteSpace {
			return
		}

		*templateStylesData = append(*templateStylesData, item)
		return
	}

	// // Generate hash attr for last item
	// if lastItem {
	// 	(*templateStylesData)[idx].Boundaries[1] = "head " + hash
	// 	hi.Attrs[hash] = ""
	// }

	isWhiteSpace := true

	for _, ch := range (*templateStylesData)[idx].HTML.Open {
		if !unicode.IsSpace(rune(ch)) {
			isWhiteSpace = false
		}
	}

	if isWhiteSpace {
		return
	}

	(*templateStylesData)[idx].HTML.Open += utils.TruncateStringWhitespace(td.Styles.Data)
}

func aggregateStyleDeps(opts *options.Options, td *parser.TemplateData, dg *parser.DepGraph, templateStyleData *[]appshell.SlotItemData, templatesSeen *map[string][]appshell.SlotItemData) {
	for _, dep := range (*dg)[td.MetaData.MetaDataPath].Deps {
		if dep.Kind.Value != "import" {
			continue
		}

		depth := ""
		for i := 0; i < td.MetaData.AdditionalDepth; i++ {
			depth += "../"
		}

		metaDataPath := filepath.Join(strings.Split(td.MetaData.MetaDataPath, filepath.Base(td.MetaData.MetaDataPath))[0], depth+strings.Split(dep.Path.RelPath, filepath.Ext(dep.Path.RelPath))[0]+".json")

		if _, ok := (*templatesSeen)[metaDataPath]; ok {
			continue
		}

		templateData := getTemplateData(opts, metaDataPath)

		appendInternalStyles(&templateData, templateStyleData, dep.Attrs["name"])
		aggregateStyleDeps(opts, &templateData, dg, templateStyleData, templatesSeen)

		(*templatesSeen)[metaDataPath] = *templateStyleData
	}
}

func compileStylesWithPostCSS(opts *options.Options, td *parser.TemplateData) string {
	if td.Styles.Data == "" {
		return ""
	}

	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	stylesPath := filepath.Join(wd, "../", "src", "compiler", "compileStyles.js")

	optsJSON, err := json.Marshal(*opts)
	if err != nil {
		log.Fatalf("\nFailed to marshal options as JSON\n%s\n", err)
	}

	var stdout bytes.Buffer
	var stderr bytes.Buffer

	cmd := exec.Command(
		"node",
		stylesPath,
		fmt.Sprintf("--options=%s", string(optsJSON)),
		fmt.Sprintf("--fromPath=%s", filepath.Join(opts.Root, td.MetaData.Path)),
		fmt.Sprintf("--css=%s", td.Styles.Data),
	)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err = cmd.Run()
	if err != nil {
		log.Fatalf("\nFailed to exec command %s\n%s\n", stylesPath, err)
	}

	fmt.Printf("%s\n", stdout.String())

	if stderr.String() != "" {
		log.Fatal(stderr.String())
	}

	return ""
}
