package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"juhe-factory/api/internal/billing"
	"juhe-factory/api/internal/model"
	queuepkg "juhe-factory/api/internal/queue"

	"github.com/google/uuid"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type ScriptAnalysisInput struct {
	Name          string `json:"name"`
	SourceContent string `json:"source_content"`
	ResultContent string `json:"result_content"`
}

type ScriptAnalysisImportProject struct {
	ID           uuid.UUID `json:"id"`
	Name         string    `json:"name"`
	EpisodeCount int       `json:"episode_count"`
	CharCount    int       `json:"char_count"`
	UpdatedAt    time.Time `json:"updated_at"`
}

func (s *Creative) ListScriptAnalyses(userID uuid.UUID) ([]model.ScriptAnalysis, error) {
	items := make([]model.ScriptAnalysis, 0)
	err := s.DB.Where("user_id=? AND deleted_at IS NULL", userID).Order("updated_at DESC").Find(&items).Error
	return items, err
}

func (s *Creative) CreateScriptAnalysis(userID uuid.UUID) (*model.ScriptAnalysis, error) {
	item := &model.ScriptAnalysis{UserID: userID, Name: "未命名剧本", AnalysisResult: json.RawMessage(`{}`), AnalysisStatus: "idle"}
	if err := s.DB.Create(item).Error; err != nil {
		return nil, err
	}
	return item, nil
}

func (s *Creative) ListScriptAnalysisImportProjects(userID uuid.UUID) ([]ScriptAnalysisImportProject, error) {
	items := make([]ScriptAnalysisImportProject, 0)
	err := s.DB.Table("creative_projects project").
		Select(`project.id,project.name,project.updated_at,
			count(episode.id) FILTER (WHERE episode.deleted_at IS NULL AND episode.status='review' AND btrim(coalesce(episode.redraw_script,'')) <> '') AS episode_count,
			coalesce(sum(char_length(episode.redraw_script)) FILTER (WHERE episode.deleted_at IS NULL AND episode.status='review' AND btrim(coalesce(episode.redraw_script,'')) <> ''),0) AS char_count`).
		Joins("LEFT JOIN project_episodes episode ON episode.project_id=project.id").
		Where(`project.user_id=? AND project.project_type='video_redraw' AND project.deleted_at IS NULL AND
			EXISTS (
				SELECT 1 FROM project_episodes source_episode
				WHERE source_episode.project_id=project.id AND source_episode.deleted_at IS NULL
					AND source_episode.status='review' AND btrim(coalesce(source_episode.redraw_script,'')) <> ''
			)`, userID).
		Group("project.id").Order("project.updated_at DESC").Scan(&items).Error
	return items, err
}

func (s *Creative) ImportScriptAnalysisProject(userID, analysisID, projectID uuid.UUID) (*model.ScriptAnalysis, error) {
	err := s.DB.Transaction(func(tx *gorm.DB) error {
		var project model.CreativeProject
		if err := tx.Select("id", "name").
			Where("id=? AND user_id=? AND project_type='video_redraw' AND deleted_at IS NULL", projectID, userID).
			Take(&project).Error; err != nil {
			return err
		}
		var episodes []model.ProjectEpisode
		if err := tx.Select("episode_no", "redraw_script").
			Where("project_id=? AND deleted_at IS NULL AND status='review' AND btrim(coalesce(redraw_script,'')) <> ''", projectID).
			Order("episode_no").Find(&episodes).Error; err != nil {
			return err
		}
		if len(episodes) == 0 {
			return errors.New("该项目暂无可导入的剧本")
		}
		parts := make([]string, 0, len(episodes))
		for _, episode := range episodes {
			parts = append(parts, fmt.Sprintf("【第%d集】\n%s", episode.EpisodeNo, strings.TrimSpace(episode.RedrawScript)))
		}
		name := project.Name
		return replaceScriptAnalysisSource(tx, userID, analysisID, name, strings.Join(parts, "\n\n"))
	})
	if err != nil {
		return nil, err
	}
	return s.GetScriptAnalysis(userID, analysisID)
}

func (s *Creative) ImportScriptAnalysisFile(userID, analysisID uuid.UUID, filename string, data []byte) (*model.ScriptAnalysis, error) {
	extension := strings.ToLower(filepath.Ext(filename))
	if extension != ".txt" && extension != ".doc" {
		return nil, errors.New("仅支持TXT和DOC文件")
	}
	if len(data) > maxDramaImportBytes {
		return nil, errors.New("剧本文件不能超过3MB")
	}
	content, _, err := parseDramaSource(filename, data, "")
	if err != nil {
		return nil, err
	}
	content = normalizeDramaText(content)
	if content == "" {
		return nil, errors.New("未读取到有效剧本正文")
	}
	name := strings.TrimSpace(strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)))
	if name == "" {
		name = "未命名剧本"
	}
	if err := s.DB.Transaction(func(tx *gorm.DB) error {
		return replaceScriptAnalysisSource(tx, userID, analysisID, name, content)
	}); err != nil {
		return nil, err
	}
	return s.GetScriptAnalysis(userID, analysisID)
}

func replaceScriptAnalysisSource(tx *gorm.DB, userID, analysisID uuid.UUID, name, content string) error {
	var item model.ScriptAnalysis
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("id=? AND user_id=? AND deleted_at IS NULL", analysisID, userID).Take(&item).Error; err != nil {
		return err
	}
	if item.AnalysisStatus == "queued" || item.AnalysisStatus == "running" {
		return errors.New("剧本正在分析，暂时不能导入")
	}
	if err := tx.Where("script_analysis_id=?", analysisID).Delete(&model.ScriptAnalysisCharacter{}).Error; err != nil {
		return err
	}
	return tx.Model(&item).Updates(map[string]any{
		"name":           strings.TrimSpace(name),
		"source_content": content, "result_content": "",
		"analysis_result": json.RawMessage(`{}`), "analysis_status": "idle", "analysis_message": "",
	}).Error
}

func (s *Creative) GetScriptAnalysis(userID, id uuid.UUID) (*model.ScriptAnalysis, error) {
	var item model.ScriptAnalysis
	if err := s.DB.Preload("Characters", func(db *gorm.DB) *gorm.DB { return db.Order("sort_order,name") }).
		Where("id=? AND user_id=? AND deleted_at IS NULL", id, userID).Take(&item).Error; err != nil {
		return nil, err
	}
	return &item, nil
}

type scriptAnalysisModelConfig struct {
	ModelID          uuid.UUID
	ChannelID        uuid.UUID
	ModelName        string
	BaseURL          string
	APIKeyCiphertext string
	Pricing          billing.TextPricing `gorm:"-"`
}

func (s *Creative) scriptAnalysisModel(userID uuid.UUID) (scriptAnalysisModelConfig, error) {
	var config scriptAnalysisModelConfig
	result := s.DB.Raw(`SELECT model.id AS model_id,channel.id AS channel_id,model.name AS model_name,
		channel.base_url,channel.api_key_ciphertext
		FROM user_model_configs preference
		JOIN models model ON model.id=preference.model_id AND model.model_type='text' AND model.enabled=true AND model.deleted_at IS NULL
		JOIN channels channel ON channel.id=model.channel_id AND channel.enabled=true AND channel.deleted_at IS NULL
		WHERE preference.user_id=? AND preference.project_type='script_analysis' AND preference.model_type='text'`, userID).Scan(&config)
	if result.Error != nil {
		return config, result.Error
	}
	if result.RowsAffected == 0 {
		return config, errors.New("请先配置剧本分析文本模型")
	}
	pricing, err := billing.LoadTextPricing(s.DB, config.ModelID)
	if err != nil {
		return config, errors.New("剧本分析文本模型计费配置无效")
	}
	config.Pricing = pricing
	return config, nil
}

func (s *Creative) QueueScriptAnalysis(userID, analysisID uuid.UUID) (*model.GenerationTask, error) {
	if s.Queue == nil {
		return nil, errors.New("剧本分析任务队列不可用")
	}
	config, err := s.scriptAnalysisModel(userID)
	if err != nil {
		return nil, err
	}
	task := &model.GenerationTask{
		RequestID: "script_analysis_" + uuid.NewString(), UserID: userID, ChannelID: &config.ChannelID,
		ModelID: &config.ModelID, ScriptAnalysisID: &analysisID, TaskType: "script_analysis", Status: "submitted",
		EstimatedPoints: "0.00", PrepaidPoints: "0.00",
	}
	err = s.DB.Transaction(func(tx *gorm.DB) error {
		var item model.ScriptAnalysis
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("id=? AND user_id=? AND deleted_at IS NULL", analysisID, userID).Take(&item).Error; err != nil {
			return err
		}
		if strings.TrimSpace(item.SourceContent) == "" {
			return errors.New("请先填写剧本原文")
		}
		var active int64
		if err := tx.Model(&model.GenerationTask{}).
			Where("script_analysis_id=? AND status IN ?", analysisID, activeTaskStatuses).Count(&active).Error; err != nil {
			return err
		}
		if active > 0 {
			return errors.New("当前剧本正在分析，请勿重复提交")
		}
		prompt, err := selectedPromptContent(tx, userID, "剧本分析")
		if err != nil {
			return err
		}
		task.PromptSnapshot = prompt
		task.InputData = mustJSON(map[string]string{"source_content": item.SourceContent})
		if err := billing.CreateTextGenerationTask(tx, task, config.Pricing, "剧本分析"); err != nil {
			return err
		}
		return tx.Model(&item).Updates(map[string]any{"analysis_status": "queued", "analysis_message": "剧本分析排队中"}).Error
	})
	if err != nil {
		return nil, err
	}
	if err := queuepkg.EnqueueID(s.Queue, queuepkg.TypeAnalyzeScript, task.ID, 0); err != nil {
		if settleErr := s.failQueuedTask(task.ID, "queue_unavailable", "剧本分析任务入队失败"); settleErr != nil {
			return nil, fmt.Errorf("剧本分析任务入队失败且预扣返还失败: %w", settleErr)
		}
		_ = s.DB.Model(&model.ScriptAnalysis{}).Where("id=?", analysisID).
			Updates(map[string]any{"analysis_status": "failed", "analysis_message": "剧本分析任务入队失败"}).Error
		return nil, errors.New("剧本分析任务队列暂时不可用，请稍后重试")
	}
	return task, nil
}

func (s *Creative) CancelScriptAnalysis(userID, analysisID uuid.UUID) error {
	return s.DB.Transaction(func(tx *gorm.DB) error {
		var task model.GenerationTask
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("script_analysis_id=? AND user_id=? AND task_type='script_analysis' AND status IN ?", analysisID, userID,
				[]string{"submitted", "processing", "cancel_requested"}).
			Order("created_at DESC").Take(&task).Error; err != nil {
			return err
		}
		if task.Status == "cancel_requested" {
			return nil
		}
		if task.Status == "submitted" {
			if err := tx.Model(&task).Updates(map[string]any{
				"status": "cancelled", "actual_points": task.PrepaidPoints, "finished_at": time.Now(),
				"error_code": "user_cancelled", "error_message": "用户取消，已扣积分不退",
			}).Error; err != nil {
				return err
			}
			return tx.Model(&model.ScriptAnalysis{}).Where("id=? AND user_id=? AND deleted_at IS NULL", analysisID, userID).Updates(map[string]any{
				"analysis_status": "idle", "analysis_message": "剧本分析已取消，已扣积分不退",
			}).Error
		}
		if err := tx.Model(&task).Updates(map[string]any{
			"status": "cancel_requested", "error_code": "user_cancelled", "error_message": "正在取消分析",
		}).Error; err != nil {
			return err
		}
		return tx.Model(&model.ScriptAnalysis{}).Where("id=? AND user_id=? AND deleted_at IS NULL", analysisID, userID).
			Update("analysis_message", "正在取消分析").Error
	})
}

func (s *Creative) UpdateScriptAnalysis(userID, id uuid.UUID, input ScriptAnalysisInput) (*model.ScriptAnalysis, error) {
	name := strings.TrimSpace(input.Name)
	if name == "" {
		return nil, errors.New("剧本名称不能为空")
	}
	result := s.DB.Model(&model.ScriptAnalysis{}).
		Where("id=? AND user_id=? AND deleted_at IS NULL", id, userID).
		Updates(map[string]any{"name": name, "source_content": input.SourceContent, "result_content": input.ResultContent})
	if result.Error != nil {
		return nil, result.Error
	}
	if result.RowsAffected == 0 {
		return nil, gorm.ErrRecordNotFound
	}
	return s.GetScriptAnalysis(userID, id)
}

func (s *Creative) DeleteScriptAnalysis(userID, id uuid.UUID) error {
	result := s.DB.Model(&model.ScriptAnalysis{}).
		Where("id=? AND user_id=? AND deleted_at IS NULL", id, userID).
		Update("deleted_at", gorm.Expr("CURRENT_TIMESTAMP"))
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}
