package usecase

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	"haixun-backend/internal/model/copy_mission/domain/entity"
	domrepo "haixun-backend/internal/model/copy_mission/domain/repository"
	domusecase "haixun-backend/internal/model/copy_mission/domain/usecase"

	"github.com/google/uuid"
)

type missionUseCase struct {
	repo domrepo.Repository
}

func NewUseCase(repo domrepo.Repository) domusecase.UseCase {
	return &missionUseCase{repo: repo}
}

func (u *missionUseCase) List(ctx context.Context, tenantID, ownerUID, personaID string) (*domusecase.ListResult, error) {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return nil, err
	}
	items, err := u.repo.ListByPersona(ctx, tenantID, ownerUID, personaID)
	if err != nil {
		return nil, err
	}
	list := make([]domusecase.MissionSummary, 0, len(items))
	for _, item := range items {
		list = append(list, toSummary(item))
	}
	return &domusecase.ListResult{List: list}, nil
}

func (u *missionUseCase) Create(ctx context.Context, req domusecase.CreateRequest) (*domusecase.MissionSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
		return nil, err
	}
	label := strings.TrimSpace(req.Label)
	seedQuery := strings.TrimSpace(req.SeedQuery)
	brief := strings.TrimSpace(req.Brief)
	if label == "" {
		return nil, app.For(code.Persona).InputMissingRequired("label is required")
	}
	if seedQuery == "" {
		return nil, app.For(code.Persona).InputMissingRequired("seed_query is required")
	}
	if brief == "" {
		return nil, app.For(code.Persona).InputMissingRequired("brief is required")
	}
	mission := &entity.Mission{
		ID:        uuid.NewString(),
		TenantID:  req.TenantID,
		OwnerUID:  req.OwnerUID,
		PersonaID: strings.TrimSpace(req.PersonaID),
		Label:     label,
		SeedQuery: seedQuery,
		Brief:     brief,
		Status:    entity.StatusOpen,
	}
	if req.InitialResearchMap != nil {
		mission.ResearchMap = *req.InitialResearchMap
	}
	if len(req.InitialSelectedTags) > 0 {
		mission.SelectedTags = cleanTags(req.InitialSelectedTags)
	}
	item, err := u.repo.Create(ctx, mission)
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *missionUseCase) Get(ctx context.Context, tenantID, ownerUID, personaID, missionID string) (*domusecase.MissionSummary, error) {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return nil, err
	}
	item, err := u.repo.FindByID(ctx, tenantID, ownerUID, personaID, missionID)
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *missionUseCase) Update(ctx context.Context, req domusecase.UpdateRequest) (*domusecase.MissionSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.PersonaID); err != nil {
		return nil, err
	}
	patch := map[string]interface{}{}
	if req.Patch.Label != nil {
		label := strings.TrimSpace(*req.Patch.Label)
		if label == "" {
			return nil, app.For(code.Persona).InputMissingRequired("label cannot be empty")
		}
		patch["label"] = label
	}
	if req.Patch.SeedQuery != nil {
		seed := strings.TrimSpace(*req.Patch.SeedQuery)
		if seed == "" {
			return nil, app.For(code.Persona).InputMissingRequired("seed_query cannot be empty")
		}
		patch["seed_query"] = seed
	}
	if req.Patch.Brief != nil {
		brief := strings.TrimSpace(*req.Patch.Brief)
		if brief == "" {
			return nil, app.For(code.Persona).InputMissingRequired("brief cannot be empty")
		}
		patch["brief"] = brief
	}
	if req.Patch.AudienceSummary != nil {
		patch["research_map.audience_summary"] = strings.TrimSpace(*req.Patch.AudienceSummary)
	}
	if req.Patch.ContentGoal != nil {
		patch["research_map.content_goal"] = strings.TrimSpace(*req.Patch.ContentGoal)
	}
	if req.Patch.QuestionsSet {
		patch["research_map.questions"] = cleanStringList(req.Patch.Questions)
	}
	if req.Patch.PillarsSet {
		patch["research_map.pillars"] = cleanStringList(req.Patch.Pillars)
	}
	if req.Patch.ExclusionsSet {
		patch["research_map.exclusions"] = cleanStringList(req.Patch.Exclusions)
	}
	if req.Patch.BenchmarkNotes != nil {
		patch["research_map.benchmark_notes"] = strings.TrimSpace(*req.Patch.BenchmarkNotes)
	}
	if req.Patch.SelectedTagsSet {
		patch["selected_tags"] = cleanTags(req.Patch.SelectedTags)
	}
	if req.Patch.ResearchMap != nil {
		patch["research_map"] = *req.Patch.ResearchMap
	}
	if req.Patch.LastScanJobID != nil {
		patch["last_scan_job_id"] = strings.TrimSpace(*req.Patch.LastScanJobID)
	}
	if req.Patch.Status != nil {
		patch["status"] = *req.Patch.Status
	}
	item, err := u.repo.Update(ctx, req.TenantID, req.OwnerUID, req.PersonaID, req.MissionID, patch)
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *missionUseCase) Delete(ctx context.Context, tenantID, ownerUID, personaID, missionID string) error {
	if err := requireActor(tenantID, ownerUID, personaID); err != nil {
		return err
	}
	return u.repo.Delete(ctx, tenantID, ownerUID, personaID, missionID)
}

func requireActor(tenantID, ownerUID, personaID string) error {
	if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
		return app.For(code.Auth).AuthUnauthorized("missing actor")
	}
	if strings.TrimSpace(personaID) == "" {
		return app.For(code.Persona).InputMissingRequired("persona_id is required")
	}
	return nil
}

func toSummary(item *entity.Mission) domusecase.MissionSummary {
	if item == nil {
		return domusecase.MissionSummary{}
	}
	tags := make([]domusecase.SuggestedTagSummary, 0, len(item.ResearchMap.SuggestedTags))
	for _, tag := range item.ResearchMap.SuggestedTags {
		tags = append(tags, domusecase.SuggestedTagSummary{
			Tag:          tag.Tag,
			Reason:       tag.Reason,
			SearchIntent: tag.SearchIntent,
			SearchType:   tag.SearchType,
		})
	}
	accounts := make([]domusecase.SimilarAccountSummary, 0, len(item.ResearchMap.SimilarAccounts))
	for _, acc := range item.ResearchMap.SimilarAccounts {
		accounts = append(accounts, domusecase.SimilarAccountSummary{
			Username:        acc.Username,
			Reason:          acc.Reason,
			Source:          acc.Source,
			Confidence:      acc.Confidence,
			ProfileURL:      acc.ProfileURL,
			AuthorVerified:  acc.AuthorVerified,
			FollowerCount:   acc.FollowerCount,
			EngagementScore: acc.EngagementScore,
			LikeCount:       acc.LikeCount,
			ReplyCount:      acc.ReplyCount,
			PostCount:       acc.PostCount,
		})
	}
	return domusecase.MissionSummary{
		ID:            item.ID,
		PersonaID:     item.PersonaID,
		Label:         item.Label,
		SeedQuery:     item.SeedQuery,
		Brief:         item.Brief,
		ResearchMap:   toMapSummary(item.ResearchMap, tags, accounts),
		SelectedTags:  append([]string(nil), item.SelectedTags...),
		LastScanJobID: item.LastScanJobID,
		Status:        string(item.Status),
		CreateAt:      item.CreateAt,
		UpdateAt:      item.UpdateAt,
	}
}

func toMapSummary(m entity.ResearchMap, tags []domusecase.SuggestedTagSummary, accounts []domusecase.SimilarAccountSummary) domusecase.ResearchMapSummary {
	return domusecase.ResearchMapSummary{
		AudienceSummary: m.AudienceSummary,
		ContentGoal:     m.ContentGoal,
		Questions:       append([]string(nil), m.Questions...),
		Pillars:         append([]string(nil), m.Pillars...),
		Exclusions:      append([]string(nil), m.Exclusions...),
		SuggestedTags:   tags,
		SimilarAccounts: accounts,
		BenchmarkNotes:  m.BenchmarkNotes,
	}
}

func cleanStringList(items []string) []string {
	out := make([]string, 0, len(items))
	seen := make(map[string]struct{}, len(items))
	for _, item := range items {
		trimmed := strings.TrimSpace(item)
		if trimmed == "" {
			continue
		}
		if _, ok := seen[trimmed]; ok {
			continue
		}
		seen[trimmed] = struct{}{}
		out = append(out, trimmed)
	}
	return out
}

func cleanTags(tags []string) []string {
	out := []string{}
	seen := map[string]struct{}{}
	for _, tag := range tags {
		tag = strings.TrimSpace(tag)
		if tag == "" {
			continue
		}
		if _, ok := seen[tag]; ok {
			continue
		}
		seen[tag] = struct{}{}
		out = append(out, tag)
	}
	return out
}
