package store

import (
	"context"
	"fmt"
	"time"

	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo/options"

	"bountyboard/internal/ulid"
)

// Notification is an in-app notification (§4.8).
type Notification struct {
	ID        string     `bson:"_id" json:"id"`
	UserID    string     `bson:"userId" json:"userId"`
	Kind      string     `bson:"kind" json:"kind"`
	Title     string     `bson:"title" json:"title"`
	Body      string     `bson:"body" json:"body"`
	Link      string     `bson:"link" json:"link"`
	ReadAt    *time.Time `bson:"readAt" json:"readAt"`
	CreatedAt time.Time  `bson:"createdAt" json:"createdAt"`
}

func (s *Store) InsertNotification(ctx context.Context, n *Notification) error {
	n.ID = ulid.New()
	n.CreatedAt = time.Now().UTC()
	if _, err := s.DB.Collection("notifications").InsertOne(ctx, n); err != nil {
		return fmt.Errorf("insert notification: %w", err)
	}
	return nil
}

func (s *Store) ListNotifications(ctx context.Context, userID string, unreadOnly bool, cursor string, limit int) ([]Notification, error) {
	if limit <= 0 || limit > 100 {
		limit = 30
	}
	q := bson.M{"userId": userID}
	if unreadOnly {
		q["readAt"] = nil
	}
	if cursor != "" {
		q["_id"] = bson.M{"$lt": cursor}
	}
	cur, err := s.DB.Collection("notifications").Find(ctx, q,
		options.Find().SetSort(bson.D{{Key: "_id", Value: -1}}).SetLimit(int64(limit)))
	if err != nil {
		return nil, fmt.Errorf("list notifications: %w", err)
	}
	out := []Notification{}
	if err := cur.All(ctx, &out); err != nil {
		return nil, fmt.Errorf("decode notifications: %w", err)
	}
	return out, nil
}

func (s *Store) CountUnreadNotifications(ctx context.Context, userID string) (int64, error) {
	n, err := s.DB.Collection("notifications").CountDocuments(ctx,
		bson.M{"userId": userID, "readAt": nil})
	if err != nil {
		return 0, fmt.Errorf("count unread: %w", err)
	}
	return n, nil
}

// MarkNotificationsRead marks the given ids (or all when ids is empty) read.
func (s *Store) MarkNotificationsRead(ctx context.Context, userID string, ids []string) (int64, error) {
	q := bson.M{"userId": userID, "readAt": nil}
	if len(ids) > 0 {
		q["_id"] = bson.M{"$in": ids}
	}
	res, err := s.DB.Collection("notifications").UpdateMany(ctx, q,
		bson.M{"$set": bson.M{"readAt": time.Now().UTC()}})
	if err != nil {
		return 0, fmt.Errorf("mark read: %w", err)
	}
	return res.ModifiedCount, nil
}
