package empyrionitems

// Template (recipe) parser for Empyrion's `Templates.ecf`.
// Format snippet:
//
//   { +Template Name: PlasticMaterial
//     BaseItem: true
//     CraftTime: 2
//     Target: "SurvC,SmallC,HoverC,BaseC,LargeC,AdvC"
//     { Child Inputs
//       CrushedStone: 1
//     }
//   }
//
// We extract Name + outer props (CraftTime, Target, BaseItem,
// OutputCount, etc.) and the Inputs map.

import (
	"bufio"
	"io"
	"os"
	"strings"
)

type Template struct {
	Name        string            `json:"name"`
	BaseItem    bool              `json:"baseItem,omitempty"`
	CraftTime   string            `json:"craftTime,omitempty"`
	Target      string            `json:"target,omitempty"`
	OutputCount string            `json:"outputCount,omitempty"`
	Ref         string            `json:"ref,omitempty"`
	Inputs      map[string]string `json:"inputs"` // ingredientName → count token
	Extras      map[string]string `json:"extras,omitempty"`
}

type TemplateIndex struct {
	Templates []Template
	byName    map[string]*Template
}

func LoadTemplatesFromFile(path string) (*TemplateIndex, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return LoadTemplatesFromReader(f)
}

func LoadTemplatesFromReader(r io.Reader) (*TemplateIndex, error) {
	idx := &TemplateIndex{byName: map[string]*Template{}}
	sc := bufio.NewScanner(r)
	sc.Buffer(make([]byte, 1024*1024), 1024*1024)

	var current *Template
	var inInputs bool
	var depth int

	for sc.Scan() {
		line := stripComment(sc.Text())
		trim := strings.TrimSpace(line)
		if trim == "" {
			continue
		}

		if current == nil {
			if strings.HasPrefix(trim, "{ +Template ") || strings.HasPrefix(trim, "{+Template ") || strings.HasPrefix(trim, "{ Template ") {
				body := strings.TrimPrefix(trim, "{ +Template ")
				body = strings.TrimPrefix(body, "{+Template ")
				body = strings.TrimPrefix(body, "{ Template ")
				body = strings.TrimSpace(body)
				current = &Template{Inputs: map[string]string{}, Extras: map[string]string{}}
				for _, kv := range splitTopLevel(body, ',') {
					parts := strings.SplitN(strings.TrimSpace(kv), ":", 2)
					if len(parts) != 2 {
						continue
					}
					k := strings.TrimSpace(parts[0])
					v := stripQuotes(strings.TrimSpace(parts[1]))
					switch strings.ToLower(k) {
					case "name":
						current.Name = v
					case "ref":
						current.Ref = v
					}
				}
				depth = 1
			}
			continue
		}

		opens := strings.Count(trim, "{")
		closes := strings.Count(trim, "}")
		depth += opens - closes
		if depth <= 0 {
			if current.Name != "" {
				cp := *current
				idx.Templates = append(idx.Templates, cp)
				idx.byName[strings.ToLower(cp.Name)] = &idx.Templates[len(idx.Templates)-1]
			}
			current = nil
			inInputs = false
			depth = 0
			continue
		}

		// Track child blocks like `{ Child Inputs`.
		if strings.HasPrefix(trim, "{ Child Inputs") || strings.HasPrefix(trim, "{Child Inputs") {
			inInputs = true
			continue
		}
		if inInputs && strings.HasPrefix(trim, "}") {
			inInputs = false
			continue
		}

		// Property line.
		parts := strings.SplitN(trim, ":", 2)
		if len(parts) != 2 {
			continue
		}
		key := strings.TrimSpace(parts[0])
		val := stripQuotes(strings.TrimSpace(parts[1]))
		val = strings.TrimSpace(stripComment(val))

		if inInputs {
			current.Inputs[key] = val
			continue
		}
		switch strings.ToLower(key) {
		case "baseitem":
			current.BaseItem = strings.EqualFold(val, "true")
		case "crafttime":
			current.CraftTime = val
		case "target":
			current.Target = val
		case "outputcount":
			current.OutputCount = val
		default:
			current.Extras[key] = val
		}
	}
	if err := sc.Err(); err != nil {
		return nil, err
	}
	return idx, nil
}

func (idx *TemplateIndex) ByName(name string) *Template {
	if t, ok := idx.byName[strings.ToLower(name)]; ok {
		c := *t
		return &c
	}
	return nil
}

// UsedIn returns the list of templates whose inputs include the named item.
func (idx *TemplateIndex) UsedIn(itemName string) []Template {
	want := strings.ToLower(itemName)
	out := []Template{}
	for _, t := range idx.Templates {
		for k := range t.Inputs {
			if strings.ToLower(k) == want {
				out = append(out, t)
				break
			}
		}
	}
	return out
}
