package repository

import (
	"context"
	"sort"
	"strings"

	"apps/backend/internal/module/radar/domain"
)

func (m *Memory) CreateSweep(_ context.Context, s *domain.RadarSweep) error {
	if s == nil {
		return domain.ErrValidation
	}
	if err := s.Normalize(); err != nil {
		return err
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	if s.ID == "" {
		s.ID = domain.NewID()
	}
	if s.StartedAt == 0 {
		s.StartedAt = domain.NowNano()
	}
	cp := *s
	cp.JudgedExternalIDs = append([]string(nil), s.JudgedExternalIDs...)
	m.sweeps[cp.ID] = &cp
	if cp.JobID != "" {
		m.jobToSweep[cp.JobID] = cp.ID
	}
	return nil
}

func (m *Memory) UpdateSweep(_ context.Context, id string, delta domain.SweepDelta) (*domain.RadarSweep, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	s, ok := m.sweeps[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	s.HitCount += delta.HitCount
	s.JudgedCount += delta.JudgedCount
	s.CreatedCount += delta.CreatedCount
	s.TruncatedCount += delta.TruncatedCount
	s.MatchEvaluatedCount += delta.MatchEvaluatedCount
	s.MatchMergedCount += delta.MatchMergedCount
	s.FitRejectedCount += delta.FitRejectedCount
	s.CreditsUsed += delta.CreditsUsed
	s.DedupedCount += delta.DedupedCount
	s.PrefilterPassCount += delta.PrefilterPassCount
	s.PrefilterReviewCount += delta.PrefilterReviewCount
	s.PrefilterRejectedCount += delta.PrefilterRejectedCount
	s.CachedJudgmentCount += delta.CachedJudgmentCount
	s.TombstoneMatchedCount += delta.TombstoneMatchedCount
	s.BudgetDeferredCount += delta.BudgetDeferredCount
	s.CreditSearch += delta.CreditSearch
	s.CreditDemandMap += delta.CreditDemandMap
	s.CreditJudge += delta.CreditJudge
	s.CreditReply += delta.CreditReply
	if len(delta.JudgedExternalIDs) > 0 {
		s.JudgedExternalIDs = domain.MergeJudgedExternalIDs(s.JudgedExternalIDs, delta.JudgedExternalIDs)
	}
	if delta.FailedReason != nil {
		s.FailedReason = strings.TrimSpace(*delta.FailedReason)
	}
	if delta.EndedAt > 0 {
		s.EndedAt = delta.EndedAt
	}
	return cloneSweep(s), nil
}

func (m *Memory) GetSweep(_ context.Context, id string) (*domain.RadarSweep, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	s, ok := m.sweeps[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	return cloneSweep(s), nil
}

func (m *Memory) GetSweepByJobID(_ context.Context, jobID string) (*domain.RadarSweep, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	id, ok := m.jobToSweep[jobID]
	if !ok {
		return nil, domain.ErrNotFound
	}
	s, ok := m.sweeps[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	return cloneSweep(s), nil
}

func (m *Memory) ListSweeps(_ context.Context, ownerUID int64, f domain.SweepListFilter) ([]*domain.RadarSweep, int64, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	matched := make([]*domain.RadarSweep, 0)
	for _, s := range m.sweeps {
		if s.OwnerUID != ownerUID {
			continue
		}
		if f.WatchID != "" && s.WatchID != f.WatchID {
			continue
		}
		matched = append(matched, cloneSweep(s))
	}
	sort.Slice(matched, func(i, j int) bool {
		if matched[i].StartedAt != matched[j].StartedAt {
			return matched[i].StartedAt > matched[j].StartedAt
		}
		return matched[i].ID < matched[j].ID
	})

	total := int64(len(matched))
	page, ps := f.Page, f.PageSize
	if page < 1 {
		page = 1
	}
	if ps < 1 {
		ps = 20
	}
	start := (page - 1) * ps
	if start >= len(matched) {
		return nil, total, nil
	}
	end := start + ps
	if end > len(matched) {
		end = len(matched)
	}
	return matched[start:end], total, nil
}

func cloneSweep(s *domain.RadarSweep) *domain.RadarSweep {
	cp := *s
	cp.JudgedExternalIDs = append([]string(nil), s.JudgedExternalIDs...)
	return &cp
}

func (m *Memory) PatchSweepPath(_ context.Context, id, path string) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	s, ok := m.sweeps[id]
	if !ok {
		return domain.ErrNotFound
	}
	s.Path = path
	return nil
}
