package repository

import (
	"context"

	"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) SaveWatch(ctx context.Context, w *domain.RadarWatch) error {
	if w == nil {
		return domain.ErrValidation
	}
	if err := w.NormalizeContext(); err != nil {
		return err
	}
	var old domain.RadarWatch
	if err := s.watches.FindOne(ctx, &old, bson.M{"_id": w.ID}); err == nil {
		if old.ContextMode == domain.WatchContextProduct && (old.BrandID != w.BrandID || old.ProductID != w.ProductID) {
			return domain.ErrValidation
		}
	} else if err != mon.ErrNotFound {
		return err
	}
	_, err := s.watches.ReplaceOne(ctx, bson.M{"_id": w.ID}, w, options.Replace().SetUpsert(true))
	return err
}

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

func (s *MonStore) DeleteWatch(ctx context.Context, id string) error {
	res, err := s.watches.DeleteOne(ctx, bson.M{"_id": id})
	if err != nil {
		return err
	}
	if res == 0 {
		return domain.ErrNotFound
	}
	return nil
}

func (s *MonStore) ListWatches(ctx context.Context, ownerUID int64, f domain.WatchListFilter) ([]*domain.RadarWatch, int64, error) {
	q := bson.M{"owner_uid": ownerUID}
	if f.Status != "" {
		q["status"] = f.Status
	}
	if f.ContextMode == domain.WatchContextGeneric {
		q["$or"] = bson.A{bson.M{"context_mode": domain.WatchContextGeneric}, bson.M{"context_mode": bson.M{"$exists": false}}, bson.M{"context_mode": ""}}
	} else if f.ContextMode != "" {
		q["context_mode"] = f.ContextMode
	}
	if f.BrandID != "" {
		q["brand_id"] = f.BrandID
	}
	if f.ProductID != "" {
		q["product_id"] = f.ProductID
	}
	total, err := s.watches.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.RadarWatch
	err = s.watches.Find(ctx, &list, q, options.Find().
		SetSort(bson.D{{Key: "created_at", Value: -1}}).
		SetSkip(int64((page-1)*ps)).
		SetLimit(int64(ps)))
	for _, w := range list {
		if w != nil && w.ContextMode == "" {
			w.ContextMode = domain.WatchContextGeneric
		}
	}
	return list, total, err
}

func (s *MonStore) ListActiveWatches(ctx context.Context, ownerUID int64) ([]*domain.RadarWatch, error) {
	var list []*domain.RadarWatch
	err := s.watches.Find(ctx, &list,
		bson.M{"owner_uid": ownerUID, "status": domain.WatchActive},
		options.Find().SetSort(bson.D{{Key: "created_at", Value: 1}}))
	return list, err
}

func (s *MonStore) ListAllActiveWatches(ctx context.Context) ([]*domain.RadarWatch, error) {
	var list []*domain.RadarWatch
	err := s.watches.Find(ctx, &list,
		bson.M{"status": domain.WatchActive},
		options.Find().SetSort(bson.D{
			{Key: "owner_uid", Value: 1},
			{Key: "created_at", Value: 1},
		}))
	return list, err
}

func (s *MonStore) CountActiveWatches(ctx context.Context, ownerUID int64) (int64, error) {
	return s.watches.CountDocuments(ctx, bson.M{"owner_uid": ownerUID, "status": domain.WatchActive})
}

// TouchWatchSweptAt 只動 last_swept_at，避免與使用者同時編輯關鍵字互相覆蓋。
func (s *MonStore) TouchWatchSweptAt(ctx context.Context, id string, at int64) error {
	res, err := s.watches.UpdateOne(ctx, bson.M{"_id": id}, bson.M{"$set": bson.M{"last_swept_at": at}})
	if err != nil {
		return err
	}
	if res.MatchedCount == 0 {
		return domain.ErrNotFound
	}
	return nil
}
