package seed

import (
	"errors"
	"strings"
	"time"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"senlinai-agent/backend/internal/models"
)

const demoTransactionAttempts = 3

type DemoOptions struct {
	Email       string
	DisplayName string
	Password    string
}

type DemoResult struct {
	User     models.SenlinAgentUser
	Projects []models.SenlinAgentProject
}

func Demo(database *gorm.DB, options DemoOptions) (DemoResult, error) {
	email := strings.ToLower(strings.TrimSpace(options.Email))
	if email == "" {
		email = "demo@senlin.ai"
	}
	displayName := strings.TrimSpace(options.DisplayName)
	if displayName == "" {
		displayName = "演示用户"
	}
	password := options.Password
	if password == "" {
		password = "password123"
	}

	var result DemoResult
	var err error
	for attempt := 0; attempt < demoTransactionAttempts; attempt++ {
		result = DemoResult{}
		err = database.Transaction(func(tx *gorm.DB) error {
			user, err := upsertAndLockUser(tx, email, displayName, password)
			if err != nil {
				return err
			}
			result.User = user

			projects, err := seedProjects(tx, user.ID)
			if err != nil {
				return err
			}
			result.Projects = projects
			return nil
		})
		if err == nil || !isRetryableDemoTransactionError(err) || attempt == demoTransactionAttempts-1 {
			return result, err
		}
		// 重试发生在事务完全回滚之后，避免持锁等待自身的新事务。
		time.Sleep(time.Duration(attempt+1) * 10 * time.Millisecond)
	}
	return result, err
}

func upsertAndLockUser(tx *gorm.DB, email string, displayName string, password string) (models.SenlinAgentUser, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return models.SenlinAgentUser{}, err
	}
	candidate := models.SenlinAgentUser{
		Email:        email,
		DisplayName:  displayName,
		PasswordHash: string(hash),
		Role:         "user",
	}
	if err := tx.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "email"}},
		DoNothing: true,
	}).Create(&candidate).Error; err != nil {
		return models.SenlinAgentUser{}, err
	}

	// PostgreSQL 会先等待并发的同 email INSERT 提交，再在 READ COMMITTED 的下一条语句中看见该用户。
	// 锁定 owner 后，项目、标签和子对象的 find-or-create 在不同进程间也会按同一用户串行执行。
	var user models.SenlinAgentUser
	err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("email = ?", email).First(&user).Error
	return user, err
}

func isRetryableDemoTransactionError(err error) bool {
	var sqlState interface{ SQLState() string }
	if !errors.As(err, &sqlState) {
		return false
	}
	switch sqlState.SQLState() {
	case "40001", "40P01":
		return true
	default:
		return false
	}
}

func seedProjects(tx *gorm.DB, ownerID uint) ([]models.SenlinAgentProject, error) {
	definitions := []struct {
		Name        string
		Identifier  string
		Description string
	}{
		{Name: "项目 A1", Identifier: "A1", Description: "企业智能协作平台的核心工作空间"},
		{Name: "项目 A2", Identifier: "A2", Description: "客户成功与交付跟踪"},
		{Name: "数据中台", Identifier: "DATA", Description: "内部数据治理与指标分析"},
		{Name: "运营自动化", Identifier: "AUTO", Description: "定时任务和业务流程自动化"},
	}

	projects := make([]models.SenlinAgentProject, 0, len(definitions))
	for _, definition := range definitions {
		project, err := firstOrCreateProject(tx, ownerID, definition.Name, definition.Identifier, definition.Description)
		if err != nil {
			return nil, err
		}
		projects = append(projects, project)
	}
	if len(projects) == 0 {
		return projects, nil
	}
	if err := seedProjectA1(tx, ownerID, projects[0].ID); err != nil {
		return nil, err
	}
	for _, project := range projects[1:] {
		if err := seedCompactProject(tx, ownerID, project.ID); err != nil {
			return nil, err
		}
	}
	return projects, nil
}

func firstOrCreateProject(tx *gorm.DB, ownerID uint, name string, identifier string, description string) (models.SenlinAgentProject, error) {
	var project models.SenlinAgentProject
	// identifier 是用户范围内的稳定唯一键；展示名称被本地修改后，重复 seed 仍复用原项目。
	err := tx.Where("owner_id = ? AND identifier = ?", ownerID, identifier).First(&project).Error
	if err == nil {
		return project, nil
	}
	if err != gorm.ErrRecordNotFound {
		return project, err
	}
	project = models.SenlinAgentProject{OwnerID: ownerID, Name: name, Identifier: identifier, Description: description}
	return project, tx.Create(&project).Error
}

func seedProjectA1(tx *gorm.DB, userID uint, projectID uint) error {
	now := time.Now().UTC()
	nextRun := now.Add(6 * time.Hour)
	dueSoon := now.Add(72 * time.Hour)
	dueLater := now.Add(7 * 24 * time.Hour)

	for _, tag := range []string{"UI", "知识碎片", "客户反馈", "自动化"} {
		if err := firstOrCreateTag(tx, projectID, tag); err != nil {
			return err
		}
	}
	uiTagID, err := tagID(tx, projectID, "UI")
	if err != nil {
		return err
	}
	feedbackTagID, err := tagID(tx, projectID, "客户反馈")
	if err != nil {
		return err
	}
	automationTagID, err := tagID(tx, projectID, "自动化")
	if err != nil {
		return err
	}
	for _, item := range []models.SenlinAgentInboxItem{
		{ProjectID: projectID, CreatedBy: userID, SourceType: "产品研究", Title: "竞争对手定价更新监控", Body: "跟进企业版价格调整和功能组合变化。", Status: "open"},
		{ProjectID: projectID, CreatedBy: userID, SourceType: "客户支持", Title: "客户反馈：导出功能报错", Body: "客户在导出智能报表时遇到权限校验异常。", Status: "open"},
		{ProjectID: projectID, CreatedBy: userID, SourceType: "架构讨论", Title: "API 调用策略评估建议", Body: "评估批量任务调度和 AI 限流策略。", Status: "open"},
		{ProjectID: projectID, CreatedBy: userID, SourceType: "需求文档", Title: "更新需求文档 v1.3.2", Body: "补充项目频道和属性弹层的交互说明。", Status: "open"},
	} {
		if err := firstOrCreateInbox(tx, item); err != nil {
			return err
		}
	}
	for _, task := range []models.SenlinAgentTask{
		{ProjectID: projectID, CreatedBy: userID, TagID: &uiTagID, Title: "智能报表导出功能", Description: "修复权限校验并补充导出失败提示。", Status: "open", DueAt: &dueSoon, SortOrder: 1},
		{ProjectID: projectID, CreatedBy: userID, TagID: &automationTagID, Title: "企业版权限体系梳理", Description: "整理角色、项目和任务分享边界。", Status: "open", DueAt: &dueLater, SortOrder: 2},
		{ProjectID: projectID, CreatedBy: userID, TagID: &feedbackTagID, Title: "Q3 营销策略制定", Description: "汇总竞品情报和客户反馈，生成策略草案。", Status: "open", DueAt: &dueLater, SortOrder: 3},
	} {
		if err := firstOrCreateTask(tx, task); err != nil {
			return err
		}
	}
	for _, session := range []models.SenlinAgentAISession{
		{ProjectID: projectID, CreatedBy: userID, Title: "生成 Q3 营销策略初稿", Context: "基于市场分析和竞品数据，输出 Q3 营销策略重点。"},
		{ProjectID: projectID, CreatedBy: userID, Title: "产品定价模型讨论", Context: "对企业版与标准版定价模型进行对比分析。"},
		{ProjectID: projectID, CreatedBy: userID, Title: "客户流失原因分析", Context: "分析近三个月客户流失数据与反馈原因。"},
		{ProjectID: projectID, CreatedBy: userID, Title: "行业趋势与机会洞察", Context: "围绕 AI 在企业协同领域的趋势与机会。"},
	} {
		if err := firstOrCreateAISession(tx, session); err != nil {
			return err
		}
	}
	for _, note := range []models.SenlinAgentNote{
		{ProjectID: projectID, CreatedBy: userID, Title: "用户权限体系设计文档 v2.1", Markdown: "# 权限体系\n\n围绕项目、任务和资料分享做保守授权。"},
		{ProjectID: projectID, CreatedBy: userID, Title: "导出功能技术方案评审", Markdown: "# 导出功能\n\n记录权限校验和任务队列方案。"},
	} {
		if err := firstOrCreateNote(tx, note); err != nil {
			return err
		}
	}
	for _, source := range []models.SenlinAgentSource{
		{ProjectID: projectID, CreatedBy: userID, Kind: "file", Title: "2026 Q3 竞品功能对比表", FilePath: "projects/a1/competitors.xlsx", ContentText: "竞品功能矩阵和价格摘要"},
		{ProjectID: projectID, CreatedBy: userID, Kind: "link", Title: "客户访谈记录索引", URL: "https://example.com/interviews", ContentText: "客户访谈和问题归类"},
	} {
		if err := firstOrCreateSource(tx, source); err != nil {
			return err
		}
	}
	for _, plan := range []models.SenlinAgentCronPlan{
		{ProjectID: projectID, CreatedBy: userID, Title: "每日 Inbox 摘要", Schedule: "0 9 * * *", NextRunAt: &nextRun, Enabled: true},
		{ProjectID: projectID, CreatedBy: userID, Title: "每周任务风险扫描", Schedule: "0 10 * * 1", NextRunAt: &dueSoon, Enabled: true},
		{ProjectID: projectID, CreatedBy: userID, Title: "月底知识归档提醒", Schedule: "0 18 28 * *", NextRunAt: &dueLater, Enabled: false},
	} {
		if err := firstOrCreateCronPlan(tx, plan); err != nil {
			return err
		}
	}
	for _, channel := range []models.SenlinAgentProjectChannel{
		{ProjectID: projectID, Title: "客户研究频道", Icon: "user", URL: "https://example.com/research", SortOrder: 7},
		{ProjectID: projectID, Title: "产品设计频道", Icon: "link", URL: "https://example.com/design", SortOrder: 8},
	} {
		if err := firstOrCreateChannel(tx, channel); err != nil {
			return err
		}
	}
	return nil
}

func seedCompactProject(tx *gorm.DB, userID uint, projectID uint) error {
	item := models.SenlinAgentInboxItem{ProjectID: projectID, CreatedBy: userID, SourceType: "手动收集", Title: "待处理事项", Body: "项目启动资料待整理。", Status: "open"}
	if err := firstOrCreateInbox(tx, item); err != nil {
		return err
	}
	task := models.SenlinAgentTask{ProjectID: projectID, CreatedBy: userID, Title: "项目例会纪要整理", Description: "归档本周同步内容。", Status: "open"}
	return firstOrCreateTask(tx, task)
}

func firstOrCreateTag(tx *gorm.DB, projectID uint, name string) error {
	candidate := models.SenlinAgentTag{ProjectID: projectID, Name: name}
	if err := tx.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "project_id"}, {Name: "name"}},
		DoNothing: true,
	}).Create(&candidate).Error; err != nil {
		return err
	}
	return tx.Where("project_id = ? AND name = ?", projectID, name).First(&models.SenlinAgentTag{}).Error
}

func tagID(tx *gorm.DB, projectID uint, name string) (uint, error) {
	var tag models.SenlinAgentTag
	if err := tx.Where("project_id = ? AND name = ?", projectID, name).First(&tag).Error; err != nil {
		return 0, err
	}
	return tag.ID, nil
}

func firstOrCreateInbox(tx *gorm.DB, item models.SenlinAgentInboxItem) error {
	return firstOrCreate(tx, &models.SenlinAgentInboxItem{}, "project_id = ? AND title = ?", []any{item.ProjectID, item.Title}, item)
}

func firstOrCreateTask(tx *gorm.DB, task models.SenlinAgentTask) error {
	var existing models.SenlinAgentTask
	err := tx.Where("project_id = ? AND title = ?", task.ProjectID, task.Title).First(&existing).Error
	if err == nil {
		if task.TagID != nil && existing.TagID == nil {
			return tx.Model(&existing).Update("tag_id", *task.TagID).Error
		}
		return nil
	}
	if err != gorm.ErrRecordNotFound {
		return err
	}
	return tx.Create(&task).Error
}

func firstOrCreateAISession(tx *gorm.DB, session models.SenlinAgentAISession) error {
	return firstOrCreate(tx, &models.SenlinAgentAISession{}, "project_id = ? AND title = ?", []any{session.ProjectID, session.Title}, session)
}

func firstOrCreateNote(tx *gorm.DB, note models.SenlinAgentNote) error {
	return firstOrCreate(tx, &models.SenlinAgentNote{}, "project_id = ? AND title = ?", []any{note.ProjectID, note.Title}, note)
}

func firstOrCreateSource(tx *gorm.DB, source models.SenlinAgentSource) error {
	return firstOrCreate(tx, &models.SenlinAgentSource{}, "project_id = ? AND title = ?", []any{source.ProjectID, source.Title}, source)
}

func firstOrCreateCronPlan(tx *gorm.DB, plan models.SenlinAgentCronPlan) error {
	return firstOrCreate(tx, &models.SenlinAgentCronPlan{}, "project_id = ? AND title = ?", []any{plan.ProjectID, plan.Title}, plan)
}

func firstOrCreateChannel(tx *gorm.DB, channel models.SenlinAgentProjectChannel) error {
	return firstOrCreate(tx, &models.SenlinAgentProjectChannel{}, "project_id = ? AND title = ?", []any{channel.ProjectID, channel.Title}, channel)
}

func firstOrCreate[T any](tx *gorm.DB, model *T, where string, args []any, create T) error {
	err := tx.Where(where, args...).First(model).Error
	if err == nil {
		return nil
	}
	if err != gorm.ErrRecordNotFound {
		return err
	}
	return tx.Create(&create).Error
}
