package main

import (
	"encoding/json"
	"fmt"
	"math"
	"os"
	"strings"
)

func loadAnnotation(path string) (AnnotationFile, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return AnnotationFile{}, err
	}

	var annotation AnnotationFile
	if err := json.Unmarshal(data, &annotation); err != nil {
		return AnnotationFile{}, err
	}

	return annotation, nil
}

func summarizeRegions(regions []Region) []RegionSummary {
	summaries := make([]RegionSummary, 0, len(regions))
	for _, region := range regions {
		bboxSource := region.BBoxSource
		if isZeroBox(bboxSource) {
			scaleFactor := region.ScaleFactor
			if scaleFactor == 0 {
				scaleFactor = 1
			}
			bboxSource = [4]float64{
				region.BBoxImage[0] / scaleFactor,
				region.BBoxImage[1] / scaleFactor,
				region.BBoxImage[2] / scaleFactor,
				region.BBoxImage[3] / scaleFactor,
			}
		}

		summaries = append(summaries, RegionSummary{
			Name:         region.Name,
			Description:  region.Description,
			Type:         region.Type,
			BBoxScreen:   region.BBoxScreen,
			BBoxSource:   bboxSource,
			CenterScreen: centerOf(region.BBoxScreen),
			CenterSource: centerOf(bboxSource),
		})
	}
	return summaries
}

func centerOf(box [4]float64) [2]float64 {
	return [2]float64{(box[0] + box[2]) / 2, (box[1] + box[3]) / 2}
}

func regionByType(summaries []RegionSummary) map[string]RegionSummary {
	index := make(map[string]RegionSummary, len(summaries))
	for _, summary := range summaries {
		if _, exists := index[summary.Type]; !exists {
			index[summary.Type] = summary
		}
	}
	return index
}

func validateRequiredRegions(summaries []RegionSummary) error {
	index := regionByType(summaries)
	missing := make([]string, 0)
	for _, regionType := range []string{"chat_content", "input_box"} {
		if _, exists := index[regionType]; !exists {
			missing = append(missing, regionType)
		}
	}
	if len(missing) > 0 {
		return fmt.Errorf("missing required regions: %v", missing)
	}
	return nil
}

func inferMissingRegionTypes(summaries []RegionSummary) ([]RegionSummary, []string) {
	index := regionByType(summaries)
	warnings := make([]string, 0)
	customs := make([]RegionSummary, 0)
	for _, summary := range summaries {
		if inferredType := inferTypeFromDescription(summary.Description); inferredType != "" {
			if _, exists := index[inferredType]; !exists {
				inferred := summary
				inferred.Type = inferredType
				inferred.Name = inferred.Name + "（按描述推断）"
				summaries = append(summaries, inferred)
				index[inferredType] = inferred
				warnings = append(warnings, fmt.Sprintf("区域 %q 已按描述推断为 %s", summary.Name, inferredType))
			}
		}

		if summary.Type == "custom" {
			customs = append(customs, summary)
		}
	}
	if len(customs) == 0 {
		return summaries, warnings
	}

	if _, exists := index["contact_list"]; !exists {
		best := customs[0]
		for _, candidate := range customs[1:] {
			if candidate.CenterScreen[0] < best.CenterScreen[0] && boxHeight(candidate.BBoxScreen) > boxHeight(best.BBoxScreen)*0.5 {
				best = candidate
			}
		}
		best.Type = "contact_list"
		best.Name = best.Name + "（推断联系人区域）"
		summaries = append(summaries, best)
		index[best.Type] = best
		warnings = append(warnings, "未标注 contact_list，已按最左侧大区域推断")
	}

	if _, exists := index["input_box"]; !exists {
		best := customs[0]
		for _, candidate := range customs[1:] {
			if candidate.CenterScreen[1] > best.CenterScreen[1] || nearlyEqual(candidate.CenterScreen[1], best.CenterScreen[1]) && boxArea(candidate.BBoxScreen) > boxArea(best.BBoxScreen) {
				best = candidate
			}
		}
		best.Type = "input_box"
		best.Name = best.Name + "（推断输入框区域）"
		summaries = append(summaries, best)
		index[best.Type] = best
		warnings = append(warnings, "未标注 input_box，已按底部区域推断")
	}

	if _, exists := index["chat_content"]; !exists {
		input := index["input_box"]
		contact := index["contact_list"]
		best := customs[0]
		bestScore := math.Inf(-1)
		for _, candidate := range customs {
			if sameBox(candidate.BBoxScreen, input.BBoxScreen) || sameBox(candidate.BBoxScreen, contact.BBoxScreen) {
				continue
			}
			score := boxArea(candidate.BBoxScreen) - math.Abs(candidate.CenterScreen[1]-input.CenterScreen[1])*0.2
			if score > bestScore {
				best = candidate
				bestScore = score
			}
		}
		best.Type = "chat_content"
		best.Name = best.Name + "（推断聊天内容区域）"
		summaries = append(summaries, best)
		index[best.Type] = best
		warnings = append(warnings, "未标注 chat_content，已按大面积内容区推断")
	}

	return summaries, warnings
}

func sameBox(a, b [4]float64) bool {
	return nearlyEqual(a[0], b[0]) && nearlyEqual(a[1], b[1]) && nearlyEqual(a[2], b[2]) && nearlyEqual(a[3], b[3])
}

func isZeroBox(box [4]float64) bool {
	return box[0] == 0 && box[1] == 0 && box[2] == 0 && box[3] == 0
}

func boxWidth(box [4]float64) float64 {
	return math.Abs(box[2] - box[0])
}

func boxHeight(box [4]float64) float64 {
	return math.Abs(box[3] - box[1])
}

func boxArea(box [4]float64) float64 {
	return boxWidth(box) * boxHeight(box)
}

func nearlyEqual(a, b float64) bool {
	return math.Abs(a-b) < 8
}

func inferTypeFromDescription(description string) string {
	text := strings.ToLower(description)
	if text == "" {
		return ""
	}

	if strings.Contains(text, "chat_content") || strings.Contains(text, "聊天") || strings.Contains(text, "消息") || strings.Contains(text, "记录") {
		return "chat_content"
	}
	if strings.Contains(text, "input_box") || strings.Contains(text, "输入") || strings.Contains(text, "文本框") || strings.Contains(text, "编辑框") {
		return "input_box"
	}
	if strings.Contains(text, "send_button") || strings.Contains(text, "发送") || strings.Contains(text, "send") {
		return "send_button"
	}
	if strings.Contains(text, "contact_list") || strings.Contains(text, "联系人") || strings.Contains(text, "会话列表") || strings.Contains(text, "好友") {
		return "contact_list"
	}

	return ""
}
