package service

import (
	"fmt"

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

type deletionMediaRow struct {
	ID        uuid.UUID
	ObjectKey string
}

type deletionMediaSet struct {
	rows map[uuid.UUID]string
}

func newDeletionMediaSet() *deletionMediaSet {
	return &deletionMediaSet{rows: make(map[uuid.UUID]string)}
}

func (set *deletionMediaSet) addQuery(query *gorm.DB) error {
	var rows []deletionMediaRow
	if err := query.Scan(&rows).Error; err != nil {
		return err
	}
	for _, row := range rows {
		if row.ID != uuid.Nil {
			set.rows[row.ID] = row.ObjectKey
		}
	}
	return nil
}

func (set *deletionMediaSet) objectKeys() []string {
	keys := make([]string, 0, len(set.rows))
	seen := make(map[string]struct{}, len(set.rows))
	for _, key := range set.rows {
		if key == "" {
			continue
		}
		if _, exists := seen[key]; exists {
			continue
		}
		seen[key] = struct{}{}
		keys = append(keys, key)
	}
	return keys
}

func (set *deletionMediaSet) deleteRows(tx *gorm.DB) error {
	if len(set.rows) == 0 {
		return nil
	}
	ids := make([]uuid.UUID, 0, len(set.rows))
	for id := range set.rows {
		ids = append(ids, id)
	}
	if err := tx.Exec("DELETE FROM channel_asset_cache WHERE media_asset_id IN ?", ids).Error; err != nil {
		return err
	}
	return tx.Exec("DELETE FROM media_assets WHERE id IN ?", ids).Error
}

func collectMediaByObjectKey(tx *gorm.DB, set *deletionMediaSet, pattern string) error {
	return set.addQuery(tx.Table("media_assets").Select("id,object_key").Where("object_key LIKE ?", pattern))
}

// removeEpisodeDerivedContent removes everything produced from an episode analysis.
// It intentionally keeps the episode source/subtitle and all project assets.
func removeEpisodeDerivedContent(tx *gorm.DB, projectID, episodeID uuid.UUID, media *deletionMediaSet) error {
	// 封面来自本剧集的首帧，删除反推结果前先清理外键引用，避免随后删除媒体对象时产生约束错误。
	if err := tx.Exec(`UPDATE creative_projects SET cover_asset_id=NULL
		WHERE id=? AND cover_asset_id=(SELECT cover_asset_id FROM project_episodes WHERE id=?)`, projectID, episodeID).Error; err != nil {
		return err
	}
	if err := tx.Exec("UPDATE project_episodes SET cover_asset_id=NULL WHERE id=?", episodeID).Error; err != nil {
		return err
	}
	taskScope := `(task_type IN ('prompt_reverse','video_generation')
		OR (task_type='image_generation' AND input_data->>'target_type'='storyboard')) AND
		(episode_id=? OR storyboard_id IN (SELECT id FROM episode_storyboards WHERE episode_id=?))`
	if err := media.addQuery(tx.Table("media_assets media").Select("DISTINCT media.id,media.object_key").
		Joins("JOIN generation_outputs output ON output.media_asset_id=media.id").
		Joins("JOIN generation_tasks task ON task.id=output.task_id").
		Where(taskScope, episodeID, episodeID)); err != nil {
		return err
	}
	if err := media.addQuery(tx.Table("media_assets media").Select("DISTINCT media.id,media.object_key").
		Joins("JOIN episode_storyboards storyboard ON storyboard.thumbnail_asset_id=media.id").
		Where("storyboard.episode_id=?", episodeID)); err != nil {
		return err
	}
	derivedPrefix := fmt.Sprintf("juyou_ran/video-redraw/projects/%s/episodes/%s/storyboards/%%", projectID, episodeID)
	if err := collectMediaByObjectKey(tx, media, derivedPrefix); err != nil {
		return err
	}
	if err := tx.Exec("UPDATE episode_storyboards SET active_output_id=NULL WHERE episode_id=?", episodeID).Error; err != nil {
		return err
	}
	if err := tx.Exec("DELETE FROM generation_outputs WHERE task_id IN (SELECT id FROM generation_tasks WHERE "+taskScope+")", episodeID, episodeID).Error; err != nil {
		return err
	}
	if err := tx.Exec("DELETE FROM generation_tasks WHERE "+taskScope, episodeID, episodeID).Error; err != nil {
		return err
	}
	if err := tx.Exec(`UPDATE generation_tasks SET storyboard_id=NULL
		WHERE storyboard_id IN (SELECT id FROM episode_storyboards WHERE episode_id=?)`, episodeID).Error; err != nil {
		return err
	}
	return tx.Exec("DELETE FROM episode_storyboards WHERE episode_id=?", episodeID).Error
}
