package repository

import (
	"context"
	"strings"

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

	"github.com/zeromicro/go-zero/core/stores/mon"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func (s *MonStore) CreateSweep(ctx context.Context, sw *domain.RadarSweep) error {
	if sw == nil {
		return domain.ErrValidation
	}
	if err := sw.Normalize(); err != nil {
		return err
	}
	if sw.ID == "" {
		sw.ID = domain.NewID()
	}
	if sw.StartedAt == 0 {
		sw.StartedAt = domain.NowNano()
	}
	_, err := s.sweeps.InsertOne(ctx, sw)
	return err
}

func (s *MonStore) UpdateSweep(ctx context.Context, id string, delta domain.SweepDelta) (*domain.RadarSweep, error) {
	inc := bson.M{}
	if delta.HitCount != 0 {
		inc["hit_count"] = delta.HitCount
	}
	if delta.JudgedCount != 0 {
		inc["judged_count"] = delta.JudgedCount
	}
	if delta.CreatedCount != 0 {
		inc["created_count"] = delta.CreatedCount
	}
	if delta.TruncatedCount != 0 {
		inc["truncated_count"] = delta.TruncatedCount
	}
	if delta.MatchEvaluatedCount != 0 {
		inc["match_evaluated_count"] = delta.MatchEvaluatedCount
	}
	if delta.MatchMergedCount != 0 {
		inc["match_merged_count"] = delta.MatchMergedCount
	}
	if delta.FitRejectedCount != 0 {
		inc["fit_rejected_count"] = delta.FitRejectedCount
	}
	if delta.CreditsUsed != 0 {
		inc["credits_used"] = delta.CreditsUsed
	}
	if delta.DedupedCount != 0 {
		inc["deduped_count"] = delta.DedupedCount
	}
	if delta.PrefilterPassCount != 0 {
		inc["prefilter_pass_count"] = delta.PrefilterPassCount
	}
	if delta.PrefilterReviewCount != 0 {
		inc["prefilter_review_count"] = delta.PrefilterReviewCount
	}
	if delta.PrefilterRejectedCount != 0 {
		inc["prefilter_rejected_count"] = delta.PrefilterRejectedCount
	}
	if delta.CachedJudgmentCount != 0 {
		inc["cached_judgment_count"] = delta.CachedJudgmentCount
	}
	if delta.TombstoneMatchedCount != 0 {
		inc["tombstone_matched_count"] = delta.TombstoneMatchedCount
	}
	if delta.BudgetDeferredCount != 0 {
		inc["budget_deferred_count"] = delta.BudgetDeferredCount
	}
	if delta.CreditSearch != 0 {
		inc["credit_search"] = delta.CreditSearch
	}
	if delta.CreditDemandMap != 0 {
		inc["credit_demand_map"] = delta.CreditDemandMap
	}
	if delta.CreditJudge != 0 {
		inc["credit_judge"] = delta.CreditJudge
	}
	if delta.CreditReply != 0 {
		inc["credit_reply"] = delta.CreditReply
	}

	update := bson.M{}
	if len(inc) > 0 {
		update["$inc"] = inc
	}
	set := bson.M{}
	if delta.FailedReason != nil {
		set["failed_reason"] = strings.TrimSpace(*delta.FailedReason)
	}
	if delta.EndedAt > 0 {
		set["ended_at"] = delta.EndedAt
	}
	if len(set) > 0 {
		update["$set"] = set
	}
	ids := domain.MergeJudgedExternalIDs(nil, delta.JudgedExternalIDs)
	if len(ids) > 0 {
		update["$addToSet"] = bson.M{"judged_external_ids": bson.M{"$each": ids}}
	}
	if len(update) == 0 {
		return s.GetSweep(ctx, id)
	}

	res, err := s.sweeps.UpdateOne(ctx, bson.M{"_id": id}, update)
	if err != nil {
		return nil, err
	}
	if res.MatchedCount == 0 {
		return nil, domain.ErrNotFound
	}
	return s.GetSweep(ctx, id)
}

func (s *MonStore) GetSweep(ctx context.Context, id string) (*domain.RadarSweep, error) {
	var sw domain.RadarSweep
	err := s.sweeps.FindOne(ctx, &sw, bson.M{"_id": id})
	if err == mon.ErrNotFound {
		return nil, domain.ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	return &sw, nil
}

func (s *MonStore) GetSweepByJobID(ctx context.Context, jobID string) (*domain.RadarSweep, error) {
	if strings.TrimSpace(jobID) == "" {
		return nil, domain.ErrNotFound
	}
	var sw domain.RadarSweep
	err := s.sweeps.FindOne(ctx, &sw, bson.M{"job_id": jobID})
	if err == mon.ErrNotFound {
		return nil, domain.ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	return &sw, nil
}

func (s *MonStore) SetSweepPath(ctx context.Context, id, path string) error {
	res, err := s.sweeps.UpdateOne(ctx, bson.M{"_id": id}, bson.M{"$set": bson.M{"path": path}})
	if err != nil {
		return err
	}
	if res.MatchedCount == 0 {
		return domain.ErrNotFound
	}
	return nil
}

func (s *MonStore) ListSweeps(ctx context.Context, ownerUID int64, f domain.SweepListFilter) ([]*domain.RadarSweep, int64, error) {
	q := bson.M{"owner_uid": ownerUID}
	if f.WatchID != "" {
		q["watch_id"] = f.WatchID
	}
	total, err := s.sweeps.CountDocuments(ctx, q)
	if err != nil {
		return nil, 0, err
	}
	page, ps := f.Page, f.PageSize
	if page < 1 {
		page = 1
	}
	if ps < 1 {
		ps = 20
	}
	var list []*domain.RadarSweep
	err = s.sweeps.Find(ctx, &list, q, options.Find().
		SetSort(bson.D{{Key: "started_at", Value: -1}}).
		SetSkip(int64((page-1)*ps)).
		SetLimit(int64(ps)))
	return list, total, err
}
