package schema

import (
	_ "embed"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
)

//go:embed marte.json
var defaultSchemaJSON []byte

type Schema struct {
	Classes map[string]ClassDefinition `json:"classes"`
}

type ClassDefinition struct {
	Fields  []FieldDefinition `json:"fields"`
	Ordered bool              `json:"ordered"`
}

type FieldDefinition struct {
	Name      string `json:"name"`
	Type      string `json:"type"` // "int", "float", "string", "bool", "reference", "array", "node", "any"
	Mandatory bool   `json:"mandatory"`
}

func NewSchema() *Schema {
	return &Schema{
		Classes: make(map[string]ClassDefinition),
	}
}

func LoadSchema(path string) (*Schema, error) {
	content, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var s Schema
	if err := json.Unmarshal(content, &s); err != nil {
		return nil, fmt.Errorf("failed to parse schema: %v", err)
	}

	return &s, nil
}

// DefaultSchema returns the built-in embedded schema
func DefaultSchema() *Schema {
	var s Schema
	if err := json.Unmarshal(defaultSchemaJSON, &s); err != nil {
		panic(fmt.Sprintf("failed to parse default embedded schema: %v", err))
	}
	if s.Classes == nil {
		s.Classes = make(map[string]ClassDefinition)
	}
	return &s
}

// Merge adds rules from 'other' to 's'.
// Rules for the same class are merged (new fields added, existing fields updated).
func (s *Schema) Merge(other *Schema) {
	if other == nil {
		return
	}
	for className, classDef := range other.Classes {
		if existingClass, ok := s.Classes[className]; ok {
			// Merge fields
			fieldMap := make(map[string]FieldDefinition)
			for _, f := range classDef.Fields {
				fieldMap[f.Name] = f
			}

			var mergedFields []FieldDefinition
			seen := make(map[string]bool)

			// Keep existing fields, update if present in other
			for _, f := range existingClass.Fields {
				if newF, ok := fieldMap[f.Name]; ok {
					mergedFields = append(mergedFields, newF)
				} else {
					mergedFields = append(mergedFields, f)
				}
				seen[f.Name] = true
			}

			// Append new fields
			for _, f := range classDef.Fields {
				if !seen[f.Name] {
					mergedFields = append(mergedFields, f)
				}
			}

			existingClass.Fields = mergedFields
			if classDef.Ordered {
				existingClass.Ordered = true
			}
			s.Classes[className] = existingClass
		} else {
			s.Classes[className] = classDef
		}
	}
}

func LoadFullSchema(projectRoot string) *Schema {
	s := DefaultSchema()

	// 1. System Paths
	sysPaths := []string{
		"/usr/share/mdt/marte_schema.json",
	}
	
	home, err := os.UserHomeDir()
	if err == nil {
		sysPaths = append(sysPaths, filepath.Join(home, ".local/share/mdt/marte_schema.json"))
	}

	for _, path := range sysPaths {
		if sysSchema, err := LoadSchema(path); err == nil {
			s.Merge(sysSchema)
		}
	}

	// 2. Project Path
	if projectRoot != "" {
		projectSchemaPath := filepath.Join(projectRoot, ".marte_schema.json")
		if projSchema, err := LoadSchema(projectSchemaPath); err == nil {
			s.Merge(projSchema)
		}
	}

	return s
}