package gen

import (
	"regexp"
	"strings"
)

type cssRule struct {
	typeOf   string
	selector string
	block    string
	ruleList cssRuleList
}

type cssRuleList []cssRule

// Prepare the css for parsing
func clean(css *string) {
	// remove spaces
	re := regexp.MustCompile(`\s+`)
	*css = re.ReplaceAllString(*css, " ")

	// remove newlines
	*css = strings.ReplaceAll(*css, "\n", "")

	// remove comments
	r := regexp.MustCompile(`/\*[\s\S]*?\*/`)
	*css = r.ReplaceAllString(*css, "")
}

// Parse the css into a ruleList
func toRuleList(css string) cssRuleList {
	clean(&css)
	dep := 0
	sel := ""
	blk := ""
	var ruleList cssRuleList
	for i := 0; i < len(css); i++ {
		chr := string(css[i])

		if chr == "{" && dep == 0 {
			dep++
			continue
		}
		if chr == "{" {
			dep++
		}

		if chr == "}" {
			dep--
		}
		if chr == "}" && dep == 0 {
			sel = strings.TrimSpace(sel)
			if strings.Contains(sel, "@media") {
				typeOf := "media"
				mediaRuleList := toRuleList(blk)
				ruleList = append(ruleList, cssRule{typeOf, sel, "", mediaRuleList})
			} else {
				typeOf := "rule"
				ruleList = append(ruleList, cssRule{typeOf, sel, blk, cssRuleList{}})
			}

			sel = ""
			blk = ""
			continue
		}

		if dep == 0 {
			sel = sel + chr
		} else {
			blk = blk + chr
		}

	}
	return ruleList
}

// scope the rules by adding a css class to each selector
func scopeSelectors(ruleList cssRuleList, className string) cssRuleList {
	newRuleList := cssRuleList{}
	for i := 0; i < len(ruleList); i++ {
		rule := &ruleList[i]

		if strings.Contains(rule.selector, ":root") {
			continue
		}
		if strings.Contains(rule.selector, ":global") {
			continue
		}
		if strings.Contains(rule.selector, "@font-face") {
			continue
		}
		if strings.Contains(rule.selector, "@import") {
			continue
		}
		if rule.typeOf == "media" {
			rule.ruleList = scopeSelectors(rule.ruleList, className)
			continue
		}

		//

		if strings.Contains(rule.selector, ":cascade") {

			newRule := cssRule{}
			newRule.selector = "." + className + " " + rule.selector
			//newRule.selector = strings.ReplaceAll(newRule.selector, ":cascade", "")
			newRule.block = rule.block
			newRuleList = append(newRuleList, newRule)

			rule.selector = "." + className + " " + rule.selector
			continue
		}
		rule.selector = strings.ReplaceAll(rule.selector, " ", "."+className+" ")
		rule.selector = rule.selector + "." + className

	}
	ruleList = append(ruleList, newRuleList...)

	return ruleList
}

func mergeMediaRules(ruleList cssRuleList) cssRuleList {
	mediaMap := make(map[string]cssRuleList)
	var newRuleList cssRuleList

	for i := 0; i < len(ruleList); i++ {
		rule := ruleList[i]
		if rule.typeOf != "media" {
			newRuleList = append(newRuleList, rule)
			continue
		}
		mediaMap[rule.selector] = append(mediaMap[rule.selector], rule.ruleList...)
	}

	for sel, rList := range mediaMap {
		newRuleList = append(newRuleList, cssRule{"media", sel, "", rList})
	}

	return newRuleList
}

// order the appRules correctly, stip the :cascade tag too
func orderRules(ruleList cssRuleList) cssRuleList {
	var newRuleList cssRuleList

	for i := 0; i < len(ruleList); i++ {
		rule := ruleList[i]
		if rule.typeOf == "media" {
			rule.ruleList = orderRules(rule.ruleList)
		}

		if strings.Contains(rule.selector, ":cascade") {
			rule.selector = strings.ReplaceAll(rule.selector, ":cascade", "")
			newRuleList = append(cssRuleList{rule}, newRuleList...)
			continue
		}

		if strings.Contains(rule.selector, ":global") {
			rule.selector = strings.ReplaceAll(rule.selector, ":global", "")
			newRuleList = append(cssRuleList{rule}, newRuleList...)
			continue
		}

		newRuleList = append(newRuleList, rule)
	}
	return newRuleList
}

func rulesToCSSstr(ruleList cssRuleList) string {
	cssStr := ""

	for i := 0; i < len(ruleList); i++ {
		rule := ruleList[i]
		if rule.typeOf == "media" {
			rule.block = rulesToCSSstr(rule.ruleList)
		}
		cssStr += rule.selector + "{" + rule.block + "}"
	}
	return cssStr
}
