package seeder

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"

	"github.com/tidwall/gjson"
	"gorm.io/gorm"
)

// rawMaterialInfo 用于临时存储解析后的原料描述、价格和最大添加量信息。
type rawMaterialInfo struct {
	Description string
	UnitPrice   float32
	MaxRatio    float32
}

// SeedNutrients 先严格校验JSON源文件，然后以“有则跳过”的模式播种数据。
func SeedNutrients(ctx context.Context, tx *gorm.DB, jsonData []byte) error {
	logger := logs.GetLogger(ctx)

	// 检查 Nutrient 表是否为空，如果非空则跳过播种
	isEmpty, err := isTableEmpty(tx, &models.Nutrient{})
	if err != nil {
		return fmt.Errorf("检查 Nutrient 表是否为空失败: %w", err)
	}
	if !isEmpty {
		logger.Info("已存在原料数据, 跳过数据播种")
		return nil
	}

	// 1. 严格校验JSON文件，检查内部重复键
	if err := validateAndParseNutrientJSON(jsonData); err != nil {
		return fmt.Errorf("JSON源文件校验失败: %w", err)
	}

	// 2. 解析简介信息
	descriptionsNode := gjson.GetBytes(jsonData, "descriptions")
	rawMaterialInfos := make(map[string]rawMaterialInfo)
	nutrientDescriptions := make(map[string]string)

	if descriptionsNode.Exists() {
		// 解析 raw_materials 描述、价格和最大添加量
		descriptionsNode.Get("raw_materials").ForEach(func(key, value gjson.Result) bool {
			rawMaterialInfos[key.String()] = rawMaterialInfo{
				Description: value.Get("descriptions").String(),
				UnitPrice:   float32(value.Get("unit_price").Float()),
				MaxRatio:    float32(value.Get("max_ratio").Float()),
			}
			return true
		})
		descriptionsNode.Get("nutrients").ForEach(func(key, value gjson.Result) bool {
			nutrientDescriptions[key.String()] = value.String()
			return true
		})
	}

	// 3. 将通过校验的、干净的数据写入数据库
	dataNode := gjson.GetBytes(jsonData, "data")
	dataNode.ForEach(func(rawMaterialKey, rawMaterialValue gjson.Result) bool {
		rawMaterialName := rawMaterialKey.String()
		var rawMaterial models.RawMaterial

		// 获取原料的描述、价格和最大添加量信息
		info := rawMaterialInfos[rawMaterialName]

		// 将 Description, ReferencePrice 和 MaxAdditionRatio 放入 Create 对象中
		err = tx.Where(models.RawMaterial{Name: rawMaterialName}).
			FirstOrCreate(&rawMaterial, models.RawMaterial{
				Name:             rawMaterialName,
				Description:      info.Description,
				ReferencePrice:   info.UnitPrice,
				MaxAdditionRatio: info.MaxRatio,
			}).Error
		if err != nil {
			// 返回 false 停止 ForEach 遍历
			return false
		}

		rawMaterialValue.ForEach(func(nutrientKey, nutrientValue gjson.Result) bool {
			nutrientName := nutrientKey.String()
			value := float32(nutrientValue.Float())

			var nutrient models.Nutrient
			// 将 Description 放入 Create 对象中
			err = tx.Where(models.Nutrient{Name: nutrientName}).
				FirstOrCreate(&nutrient, models.Nutrient{
					Name:        nutrientName,
					Description: nutrientDescriptions[nutrientName],
				}).Error
			if err != nil {
				// 返回 false 停止 ForEach 遍历
				return false
			}

			linkData := models.RawMaterialNutrient{
				RawMaterialID: rawMaterial.ID,
				NutrientID:    nutrient.ID,
			}
			// 使用 FirstOrCreate 确保关联的唯一性
			err = tx.Where(linkData).FirstOrCreate(&linkData, models.RawMaterialNutrient{
				RawMaterialID: linkData.RawMaterialID,
				NutrientID:    linkData.NutrientID,
				Value:         value,
			}).Error
			if err != nil {
				// 返回 false 停止 ForEach 遍历
				return false
			}
			return true
		})
		return err == nil // 如果内部遍历有错误，则停止外部遍历
	})

	return err // 返回捕获到的错误
}

// validateAndParseNutrientJSON 严格校验JSON文件
func validateAndParseNutrientJSON(jsonData []byte) error {
	descriptionsNode := gjson.GetBytes(jsonData, "descriptions")
	if !descriptionsNode.Exists() {
		return errors.New("JSON文件中缺少 'descriptions' 字段")
	}
	if !descriptionsNode.IsObject() {
		return errors.New("'descriptions' 字段必须是一个JSON对象")
	}

	rawMaterialsNode := descriptionsNode.Get("raw_materials")
	if !rawMaterialsNode.Exists() {
		return errors.New("JSON文件中缺少 'descriptions.raw_materials' 字段")
	}
	if !rawMaterialsNode.IsObject() {
		return errors.New("'descriptions.raw_materials' 字段必须是一个JSON对象")
	}

	// 使用 json.Decoder 严格校验 raw_materials 的结构
	decoder := json.NewDecoder(bytes.NewReader([]byte(rawMaterialsNode.Raw)))
	decoder.UseNumber()

	if t, err := decoder.Token(); err != nil || t != json.Delim('{') {
		return fmt.Errorf("'descriptions.raw_materials' 字段解析起始符失败: %v", err)
	}

	seenRawMaterials := make(map[string]bool)

	for decoder.More() {
		// 1. 解析原料名称
		t, err := decoder.Token()
		if err != nil {
			return fmt.Errorf("解析原料名称失败: %w", err)
		}
		rawMaterialName := t.(string)
		if seenRawMaterials[rawMaterialName] {
			return fmt.Errorf("原料名称 '%s' 重复", rawMaterialName)
		}
		seenRawMaterials[rawMaterialName] = true

		// 2. 解析该原料的描述和价格对象
		if t, err := decoder.Token(); err != nil || t != json.Delim('{') {
			return fmt.Errorf("期望原料 '%s' 的值是一个JSON对象", rawMaterialName)
		}

		for decoder.More() {
			t, err := decoder.Token()
			if err != nil {
				return fmt.Errorf("解析原料 '%s' 内部键失败: %w", rawMaterialName, err)
			}
			key := t.(string)

			switch key {
			case "descriptions":
				t, err = decoder.Token()
				if err != nil {
					return fmt.Errorf("解析原料 '%s' 的 'descriptions' 值失败: %w", rawMaterialName, err)
				}
				if _, ok := t.(string); !ok {
					return fmt.Errorf("期望原料 '%s' 的 'descriptions' 值是字符串, 但实际得到的类型是 %T, 值为 '%v'", rawMaterialName, t, t)
				}
			case "unit_price":
				t, err = decoder.Token()
				if err != nil {
					return fmt.Errorf("解析原料 '%s' 的 'unit_price' 值失败: %w", rawMaterialName, err)
				}
				if _, ok := t.(json.Number); !ok {
					return fmt.Errorf("期望原料 '%s' 的 'unit_price' 值是数字, 但实际得到的类型是 %T, 值为 '%v'", rawMaterialName, t, t)
				}
			case "max_ratio":
				t, err = decoder.Token()
				if err != nil {
					return fmt.Errorf("解析原料 '%s' 的 'max_ratio' 值失败: %w", rawMaterialName, err)
				}
				if _, ok := t.(json.Number); !ok {
					return fmt.Errorf("期望原料 '%s' 的 'max_ratio' 值是数字, 但实际得到的类型是 %T, 值为 '%v'", rawMaterialName, t, t)
				}
			default:
				// 忽略其他未知字段，但仍需读取其值以继续解析
				if _, err := decoder.Token(); err != nil {
					return fmt.Errorf("解析原料 '%s' 的未知键 '%s' 的值失败: %w", rawMaterialName, key, err)
				}
			}
		}

		// 读取原料描述和价格对象的 "}"
		if t, err := decoder.Token(); err != nil || t != json.Delim('}') {
			return fmt.Errorf("解析原料 '%s' 的值结束符 '}' 失败", rawMaterialName)
		}
	}

	// 校验 data 节点
	dataNode := gjson.GetBytes(jsonData, "data")
	if !dataNode.Exists() {
		return errors.New("JSON文件中缺少 'data' 字段")
	}
	if !dataNode.IsObject() {
		return errors.New("'data' 字段必须是一个JSON对象")
	}

	// 重新初始化 decoder 用于 data 节点的校验
	decoder = json.NewDecoder(bytes.NewReader([]byte(dataNode.Raw)))
	decoder.UseNumber()
	if t, err := decoder.Token(); err != nil || t != json.Delim('{') {
		return errors.New("'data' 字段解析起始符失败")
	}

	seenRawMaterials = make(map[string]bool) // 重置 seenRawMaterials 用于 data 节点校验

	for decoder.More() {
		// 1. 解析原料名称
		t, err := decoder.Token()
		if err != nil {
			return fmt.Errorf("解析原料名称失败: %w", err)
		}
		rawMaterialName := t.(string)
		if seenRawMaterials[rawMaterialName] {
			return fmt.Errorf("原料名称 '%s' 重复", rawMaterialName)
		}
		seenRawMaterials[rawMaterialName] = true

		// 2. 解析该原料的营养成分对象
		if t, err := decoder.Token(); err != nil || t != json.Delim('{') {
			return fmt.Errorf("期望原料 '%s' 的值是一个JSON对象", rawMaterialName)
		}

		seenNutrients := make(map[string]bool)
		for decoder.More() {
			// 解析营养素名称
			t, err := decoder.Token()
			if err != nil {
				return fmt.Errorf("在原料 '%s' 中解析营养素名称失败: %w", rawMaterialName, err)
			}
			nutrientName := t.(string)
			if seenNutrients[nutrientName] {
				return fmt.Errorf("在原料 '%s' 中, 营养素名称 '%s' 重复", rawMaterialName, nutrientName)
			}
			seenNutrients[nutrientName] = true

			// 解析营养素含量
			t, err = decoder.Token()
			if err != nil {
				return fmt.Errorf("在原料 '%s' 中解析营养素 '%s' 的含量值失败: %w", rawMaterialName, nutrientName, err)
			}
			if _, ok := t.(json.Number); !ok {
				return fmt.Errorf("期望营养素 '%s' 的含量值是数字, 但实际得到的类型是 %T, 值为 '%v'", nutrientName, t, t)
			}
		}

		// 读取营养成分对象的 "}"
		if t, err := decoder.Token(); err != nil || t != json.Delim('}') {
			return fmt.Errorf("解析原料 '%s' 的值结束符 '}' 失败", rawMaterialName)
		}
	}
	return nil
}
