package initdb

import (
	_ "embed"
	"encoding/json"
	"fmt"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"senlinai-agent/backend/internal/models"
)

//go:embed experts_catalog.json
var expertCatalogJSON []byte

type expertCatalog struct {
	Source    string              `json:"source"`
	Reference string              `json:"reference"`
	License   string              `json:"license"`
	Experts   []expertCatalogItem `json:"experts"`
}

type expertCatalogItem struct {
	Slug         string `json:"slug"`
	Category     string `json:"category"`
	CategoryName string `json:"categoryName"`
	Name         string `json:"name"`
	Description  string `json:"description"`
	Emoji        string `json:"emoji"`
	Color        string `json:"color"`
	SystemPrompt string `json:"systemPrompt"`
}

// InitExpert inserts categories and expert items that are missing from the catalog tables.
func InitExpert(database *gorm.DB) error {
	var catalog expertCatalog
	if err := json.Unmarshal(expertCatalogJSON, &catalog); err != nil {
		return fmt.Errorf("decode embedded AI expert catalog: %w", err)
	}

	return database.Transaction(func(tx *gorm.DB) error {
		categories, err := initExpertCategories(tx, catalog.Experts)
		if err != nil {
			return err
		}
		return initExpertItems(tx, catalog, categories)
	})
}

func initExpertCategories(tx *gorm.DB, experts []expertCatalogItem) (map[string]models.SaAIExpertCategory, error) {
	categoryNames := make(map[string]string)
	categoryOrder := make([]string, 0)
	for _, expert := range experts {
		if _, exists := categoryNames[expert.Category]; exists {
			continue
		}
		categoryNames[expert.Category] = expert.CategoryName
		categoryOrder = append(categoryOrder, expert.Category)
	}

	records := make([]models.SaAIExpertCategory, 0, len(categoryOrder))
	for _, slug := range categoryOrder {
		records = append(records, models.SaAIExpertCategory{
			Slug: slug,
			Name: categoryNames[slug],
		})
	}
	if len(records) > 0 {
		if err := tx.Clauses(clause.OnConflict{
			Columns:   []clause.Column{{Name: "slug"}},
			DoNothing: true,
		}).Create(&records).Error; err != nil {
			return nil, err
		}
	}

	var stored []models.SaAIExpertCategory
	if err := tx.Where("slug IN ?", categoryOrder).Find(&stored).Error; err != nil {
		return nil, err
	}
	categories := make(map[string]models.SaAIExpertCategory, len(stored))
	for _, category := range stored {
		categories[category.Slug] = category
	}
	if len(categories) != len(categoryOrder) {
		return nil, fmt.Errorf("initialize AI expert categories: expected %d, found %d", len(categoryOrder), len(categories))
	}
	return categories, nil
}

func initExpertItems(tx *gorm.DB, catalog expertCatalog, categories map[string]models.SaAIExpertCategory) error {
	records := make([]models.SaAIExpertItem, 0, len(catalog.Experts))
	for _, expert := range catalog.Experts {
		category, exists := categories[expert.Category]
		if !exists {
			return fmt.Errorf("initialize AI expert item %q: category %q not found", expert.Slug, expert.Category)
		}
		records = append(records, models.SaAIExpertItem{
			Slug: expert.Slug, CategoryID: category.ID, CategoryIdentity: category.Identity,
			Category: expert.Category, CategoryName: expert.CategoryName,
			Name: expert.Name, Description: expert.Description, Emoji: expert.Emoji,
			Color: expert.Color, SystemPrompt: expert.SystemPrompt, Source: catalog.Source,
			Reference: catalog.Reference, SourceLicense: catalog.License, Enabled: true,
		})
	}
	if len(records) == 0 {
		return nil
	}
	return tx.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "slug"}},
		DoUpdates: clause.AssignmentColumns([]string{
			"category_id",
			"category_identity",
			"reference",
		}),
	}).CreateInBatches(&records, 25).Error
}
