//go:build integration

package repository

import (
	"context"
	"testing"
	"time"

	"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
	"github.com/Wei-Shaw/sub2api/internal/service"
	"github.com/stretchr/testify/suite"
	"gorm.io/gorm"
)

type UserSubscriptionRepoSuite struct {
	suite.Suite
	ctx  context.Context
	db   *gorm.DB
	repo *userSubscriptionRepository
}

func (s *UserSubscriptionRepoSuite) SetupTest() {
	s.ctx = context.Background()
	s.db = testTx(s.T())
	s.repo = NewUserSubscriptionRepository(s.db).(*userSubscriptionRepository)
}

func TestUserSubscriptionRepoSuite(t *testing.T) {
	suite.Run(t, new(UserSubscriptionRepoSuite))
}

// --- Create / GetByID / Update / Delete ---

func (s *UserSubscriptionRepoSuite) TestCreate() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "sub-create@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-create"})

	sub := &service.UserSubscription{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	}

	err := s.repo.Create(s.ctx, sub)
	s.Require().NoError(err, "Create")
	s.Require().NotZero(sub.ID, "expected ID to be set")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err, "GetByID")
	s.Require().Equal(sub.UserID, got.UserID)
	s.Require().Equal(sub.GroupID, got.GroupID)
}

func (s *UserSubscriptionRepoSuite) TestGetByID_WithPreloads() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "preload@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-preload"})
	admin := mustCreateUser(s.T(), s.db, &userModel{Email: "admin@test.com", Role: service.RoleAdmin})

	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:     user.ID,
		GroupID:    group.ID,
		Status:     service.SubscriptionStatusActive,
		ExpiresAt:  time.Now().Add(24 * time.Hour),
		AssignedBy: &admin.ID,
	})

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err, "GetByID")
	s.Require().NotNil(got.User, "expected User preload")
	s.Require().NotNil(got.Group, "expected Group preload")
	s.Require().NotNil(got.AssignedByUser, "expected AssignedByUser preload")
	s.Require().Equal(user.ID, got.User.ID)
	s.Require().Equal(group.ID, got.Group.ID)
	s.Require().Equal(admin.ID, got.AssignedByUser.ID)
}

func (s *UserSubscriptionRepoSuite) TestGetByID_NotFound() {
	_, err := s.repo.GetByID(s.ctx, 999999)
	s.Require().Error(err, "expected error for non-existent ID")
}

func (s *UserSubscriptionRepoSuite) TestUpdate() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "update@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-update"})
	sub := userSubscriptionModelToService(mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	}))

	sub.Notes = "updated notes"
	err := s.repo.Update(s.ctx, sub)
	s.Require().NoError(err, "Update")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err, "GetByID after update")
	s.Require().Equal("updated notes", got.Notes)
}

func (s *UserSubscriptionRepoSuite) TestDelete() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "delete@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-delete"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	err := s.repo.Delete(s.ctx, sub.ID)
	s.Require().NoError(err, "Delete")

	_, err = s.repo.GetByID(s.ctx, sub.ID)
	s.Require().Error(err, "expected error after delete")
}

// --- GetByUserIDAndGroupID / GetActiveByUserIDAndGroupID ---

func (s *UserSubscriptionRepoSuite) TestGetByUserIDAndGroupID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "byuser@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-byuser"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	got, err := s.repo.GetByUserIDAndGroupID(s.ctx, user.ID, group.ID)
	s.Require().NoError(err, "GetByUserIDAndGroupID")
	s.Require().Equal(sub.ID, got.ID)
	s.Require().NotNil(got.Group, "expected Group preload")
}

func (s *UserSubscriptionRepoSuite) TestGetByUserIDAndGroupID_NotFound() {
	_, err := s.repo.GetByUserIDAndGroupID(s.ctx, 999999, 999999)
	s.Require().Error(err, "expected error for non-existent pair")
}

func (s *UserSubscriptionRepoSuite) TestGetActiveByUserIDAndGroupID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "active@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-active"})

	// Create active subscription (future expiry)
	active := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(2 * time.Hour),
	})

	got, err := s.repo.GetActiveByUserIDAndGroupID(s.ctx, user.ID, group.ID)
	s.Require().NoError(err, "GetActiveByUserIDAndGroupID")
	s.Require().Equal(active.ID, got.ID)
}

func (s *UserSubscriptionRepoSuite) TestGetActiveByUserIDAndGroupID_ExpiredIgnored() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "expired@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-expired"})

	// Create expired subscription (past expiry but active status)
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(-2 * time.Hour),
	})

	_, err := s.repo.GetActiveByUserIDAndGroupID(s.ctx, user.ID, group.ID)
	s.Require().Error(err, "expected error for expired subscription")
}

// --- ListByUserID / ListActiveByUserID ---

func (s *UserSubscriptionRepoSuite) TestListByUserID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "listby@test.com"})
	g1 := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-list1"})
	g2 := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-list2"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   g1.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   g2.ID,
		Status:    service.SubscriptionStatusExpired,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	subs, err := s.repo.ListByUserID(s.ctx, user.ID)
	s.Require().NoError(err, "ListByUserID")
	s.Require().Len(subs, 2)
	for _, sub := range subs {
		s.Require().NotNil(sub.Group, "expected Group preload")
	}
}

func (s *UserSubscriptionRepoSuite) TestListActiveByUserID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "listactive@test.com"})
	g1 := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-act1"})
	g2 := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-act2"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   g1.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   g2.ID,
		Status:    service.SubscriptionStatusExpired,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	subs, err := s.repo.ListActiveByUserID(s.ctx, user.ID)
	s.Require().NoError(err, "ListActiveByUserID")
	s.Require().Len(subs, 1)
	s.Require().Equal(service.SubscriptionStatusActive, subs[0].Status)
}

// --- ListByGroupID ---

func (s *UserSubscriptionRepoSuite) TestListByGroupID() {
	user1 := mustCreateUser(s.T(), s.db, &userModel{Email: "u1@test.com"})
	user2 := mustCreateUser(s.T(), s.db, &userModel{Email: "u2@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-listgrp"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user1.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user2.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	subs, page, err := s.repo.ListByGroupID(s.ctx, group.ID, pagination.PaginationParams{Page: 1, PageSize: 10})
	s.Require().NoError(err, "ListByGroupID")
	s.Require().Len(subs, 2)
	s.Require().Equal(int64(2), page.Total)
	for _, sub := range subs {
		s.Require().NotNil(sub.User, "expected User preload")
		s.Require().NotNil(sub.Group, "expected Group preload")
	}
}

// --- List with filters ---

func (s *UserSubscriptionRepoSuite) TestList_NoFilters() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "list@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-list"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	subs, page, err := s.repo.List(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, nil, nil, "")
	s.Require().NoError(err, "List")
	s.Require().Len(subs, 1)
	s.Require().Equal(int64(1), page.Total)
}

func (s *UserSubscriptionRepoSuite) TestList_FilterByUserID() {
	user1 := mustCreateUser(s.T(), s.db, &userModel{Email: "filter1@test.com"})
	user2 := mustCreateUser(s.T(), s.db, &userModel{Email: "filter2@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-filter"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user1.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user2.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	subs, _, err := s.repo.List(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, &user1.ID, nil, "")
	s.Require().NoError(err)
	s.Require().Len(subs, 1)
	s.Require().Equal(user1.ID, subs[0].UserID)
}

func (s *UserSubscriptionRepoSuite) TestList_FilterByGroupID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "grpfilter@test.com"})
	g1 := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-f1"})
	g2 := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-f2"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   g1.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   g2.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	subs, _, err := s.repo.List(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, nil, &g1.ID, "")
	s.Require().NoError(err)
	s.Require().Len(subs, 1)
	s.Require().Equal(g1.ID, subs[0].GroupID)
}

func (s *UserSubscriptionRepoSuite) TestList_FilterByStatus() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "statfilter@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-stat"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusExpired,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	subs, _, err := s.repo.List(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, nil, nil, service.SubscriptionStatusExpired)
	s.Require().NoError(err)
	s.Require().Len(subs, 1)
	s.Require().Equal(service.SubscriptionStatusExpired, subs[0].Status)
}

// --- Usage tracking ---

func (s *UserSubscriptionRepoSuite) TestIncrementUsage() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "usage@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-usage"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	err := s.repo.IncrementUsage(s.ctx, sub.ID, 1.25)
	s.Require().NoError(err, "IncrementUsage")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Equal(1.25, got.DailyUsageUSD)
	s.Require().Equal(1.25, got.WeeklyUsageUSD)
	s.Require().Equal(1.25, got.MonthlyUsageUSD)
}

func (s *UserSubscriptionRepoSuite) TestIncrementUsage_Accumulates() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "accum@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-accum"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	s.Require().NoError(s.repo.IncrementUsage(s.ctx, sub.ID, 1.0))
	s.Require().NoError(s.repo.IncrementUsage(s.ctx, sub.ID, 2.5))

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Equal(3.5, got.DailyUsageUSD)
}

func (s *UserSubscriptionRepoSuite) TestActivateWindows() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "activate@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-activate"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	activateAt := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
	err := s.repo.ActivateWindows(s.ctx, sub.ID, activateAt)
	s.Require().NoError(err, "ActivateWindows")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().NotNil(got.DailyWindowStart)
	s.Require().NotNil(got.WeeklyWindowStart)
	s.Require().NotNil(got.MonthlyWindowStart)
	s.Require().True(got.DailyWindowStart.Equal(activateAt))
}

func (s *UserSubscriptionRepoSuite) TestResetDailyUsage() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "resetd@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-resetd"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:         user.ID,
		GroupID:        group.ID,
		Status:         service.SubscriptionStatusActive,
		ExpiresAt:      time.Now().Add(24 * time.Hour),
		DailyUsageUSD:  10.0,
		WeeklyUsageUSD: 20.0,
	})

	resetAt := time.Date(2025, 1, 2, 0, 0, 0, 0, time.UTC)
	err := s.repo.ResetDailyUsage(s.ctx, sub.ID, resetAt)
	s.Require().NoError(err, "ResetDailyUsage")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Zero(got.DailyUsageUSD)
	s.Require().Equal(20.0, got.WeeklyUsageUSD, "weekly should remain unchanged")
	s.Require().True(got.DailyWindowStart.Equal(resetAt))
}

func (s *UserSubscriptionRepoSuite) TestResetWeeklyUsage() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "resetw@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-resetw"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:          user.ID,
		GroupID:         group.ID,
		Status:          service.SubscriptionStatusActive,
		ExpiresAt:       time.Now().Add(24 * time.Hour),
		WeeklyUsageUSD:  15.0,
		MonthlyUsageUSD: 30.0,
	})

	resetAt := time.Date(2025, 1, 6, 0, 0, 0, 0, time.UTC)
	err := s.repo.ResetWeeklyUsage(s.ctx, sub.ID, resetAt)
	s.Require().NoError(err, "ResetWeeklyUsage")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Zero(got.WeeklyUsageUSD)
	s.Require().Equal(30.0, got.MonthlyUsageUSD, "monthly should remain unchanged")
	s.Require().True(got.WeeklyWindowStart.Equal(resetAt))
}

func (s *UserSubscriptionRepoSuite) TestResetMonthlyUsage() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "resetm@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-resetm"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:          user.ID,
		GroupID:         group.ID,
		Status:          service.SubscriptionStatusActive,
		ExpiresAt:       time.Now().Add(24 * time.Hour),
		MonthlyUsageUSD: 100.0,
	})

	resetAt := time.Date(2025, 2, 1, 0, 0, 0, 0, time.UTC)
	err := s.repo.ResetMonthlyUsage(s.ctx, sub.ID, resetAt)
	s.Require().NoError(err, "ResetMonthlyUsage")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Zero(got.MonthlyUsageUSD)
	s.Require().True(got.MonthlyWindowStart.Equal(resetAt))
}

// --- UpdateStatus / ExtendExpiry / UpdateNotes ---

func (s *UserSubscriptionRepoSuite) TestUpdateStatus() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "status@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-status"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	err := s.repo.UpdateStatus(s.ctx, sub.ID, service.SubscriptionStatusExpired)
	s.Require().NoError(err, "UpdateStatus")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Equal(service.SubscriptionStatusExpired, got.Status)
}

func (s *UserSubscriptionRepoSuite) TestExtendExpiry() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "extend@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-extend"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	newExpiry := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
	err := s.repo.ExtendExpiry(s.ctx, sub.ID, newExpiry)
	s.Require().NoError(err, "ExtendExpiry")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().True(got.ExpiresAt.Equal(newExpiry))
}

func (s *UserSubscriptionRepoSuite) TestUpdateNotes() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "notes@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-notes"})
	sub := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	err := s.repo.UpdateNotes(s.ctx, sub.ID, "VIP user")
	s.Require().NoError(err, "UpdateNotes")

	got, err := s.repo.GetByID(s.ctx, sub.ID)
	s.Require().NoError(err)
	s.Require().Equal("VIP user", got.Notes)
}

// --- ListExpired / BatchUpdateExpiredStatus ---

func (s *UserSubscriptionRepoSuite) TestListExpired() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "listexp@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-listexp"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	expired, err := s.repo.ListExpired(s.ctx)
	s.Require().NoError(err, "ListExpired")
	s.Require().Len(expired, 1)
}

func (s *UserSubscriptionRepoSuite) TestBatchUpdateExpiredStatus() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "batch@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-batch"})

	active := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	expiredActive := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	affected, err := s.repo.BatchUpdateExpiredStatus(s.ctx)
	s.Require().NoError(err, "BatchUpdateExpiredStatus")
	s.Require().Equal(int64(1), affected)

	gotActive, _ := s.repo.GetByID(s.ctx, active.ID)
	s.Require().Equal(service.SubscriptionStatusActive, gotActive.Status)

	gotExpired, _ := s.repo.GetByID(s.ctx, expiredActive.ID)
	s.Require().Equal(service.SubscriptionStatusExpired, gotExpired.Status)
}

// --- ExistsByUserIDAndGroupID ---

func (s *UserSubscriptionRepoSuite) TestExistsByUserIDAndGroupID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "exists@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-exists"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})

	exists, err := s.repo.ExistsByUserIDAndGroupID(s.ctx, user.ID, group.ID)
	s.Require().NoError(err, "ExistsByUserIDAndGroupID")
	s.Require().True(exists)

	notExists, err := s.repo.ExistsByUserIDAndGroupID(s.ctx, user.ID, 999999)
	s.Require().NoError(err)
	s.Require().False(notExists)
}

// --- CountByGroupID / CountActiveByGroupID ---

func (s *UserSubscriptionRepoSuite) TestCountByGroupID() {
	user1 := mustCreateUser(s.T(), s.db, &userModel{Email: "cnt1@test.com"})
	user2 := mustCreateUser(s.T(), s.db, &userModel{Email: "cnt2@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-count"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user1.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user2.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusExpired,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	count, err := s.repo.CountByGroupID(s.ctx, group.ID)
	s.Require().NoError(err, "CountByGroupID")
	s.Require().Equal(int64(2), count)
}

func (s *UserSubscriptionRepoSuite) TestCountActiveByGroupID() {
	user1 := mustCreateUser(s.T(), s.db, &userModel{Email: "cntact1@test.com"})
	user2 := mustCreateUser(s.T(), s.db, &userModel{Email: "cntact2@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-cntact"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user1.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user2.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(-24 * time.Hour), // expired by time
	})

	count, err := s.repo.CountActiveByGroupID(s.ctx, group.ID)
	s.Require().NoError(err, "CountActiveByGroupID")
	s.Require().Equal(int64(1), count, "only future expiry counts as active")
}

// --- DeleteByGroupID ---

func (s *UserSubscriptionRepoSuite) TestDeleteByGroupID() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "delgrp@test.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-delgrp"})

	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(24 * time.Hour),
	})
	mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusExpired,
		ExpiresAt: time.Now().Add(-24 * time.Hour),
	})

	affected, err := s.repo.DeleteByGroupID(s.ctx, group.ID)
	s.Require().NoError(err, "DeleteByGroupID")
	s.Require().Equal(int64(2), affected)

	count, _ := s.repo.CountByGroupID(s.ctx, group.ID)
	s.Require().Zero(count)
}

// --- Combined original test ---

func (s *UserSubscriptionRepoSuite) TestActiveExpiredBoundaries_UsageAndReset_BatchUpdateExpiredStatus() {
	user := mustCreateUser(s.T(), s.db, &userModel{Email: "subr@example.com"})
	group := mustCreateGroup(s.T(), s.db, &groupModel{Name: "g-subr"})

	active := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(2 * time.Hour),
	})
	expiredActive := mustCreateSubscription(s.T(), s.db, &userSubscriptionModel{
		UserID:    user.ID,
		GroupID:   group.ID,
		Status:    service.SubscriptionStatusActive,
		ExpiresAt: time.Now().Add(-2 * time.Hour),
	})

	got, err := s.repo.GetActiveByUserIDAndGroupID(s.ctx, user.ID, group.ID)
	s.Require().NoError(err, "GetActiveByUserIDAndGroupID")
	s.Require().Equal(active.ID, got.ID, "expected active subscription")

	activateAt := time.Now().Add(-25 * time.Hour)
	s.Require().NoError(s.repo.ActivateWindows(s.ctx, active.ID, activateAt), "ActivateWindows")
	s.Require().NoError(s.repo.IncrementUsage(s.ctx, active.ID, 1.25), "IncrementUsage")

	after, err := s.repo.GetByID(s.ctx, active.ID)
	s.Require().NoError(err, "GetByID")
	s.Require().Equal(1.25, after.DailyUsageUSD, "DailyUsageUSD mismatch")
	s.Require().Equal(1.25, after.WeeklyUsageUSD, "WeeklyUsageUSD mismatch")
	s.Require().Equal(1.25, after.MonthlyUsageUSD, "MonthlyUsageUSD mismatch")
	s.Require().NotNil(after.DailyWindowStart, "expected DailyWindowStart activated")
	s.Require().NotNil(after.WeeklyWindowStart, "expected WeeklyWindowStart activated")
	s.Require().NotNil(after.MonthlyWindowStart, "expected MonthlyWindowStart activated")

	resetAt := time.Now().Truncate(time.Microsecond) // truncate to microsecond for DB precision
	s.Require().NoError(s.repo.ResetDailyUsage(s.ctx, active.ID, resetAt), "ResetDailyUsage")
	afterReset, err := s.repo.GetByID(s.ctx, active.ID)
	s.Require().NoError(err, "GetByID after reset")
	s.Require().Equal(0.0, afterReset.DailyUsageUSD, "expected daily usage reset to 0")
	s.Require().NotNil(afterReset.DailyWindowStart, "expected DailyWindowStart not nil")
	s.Require().True(afterReset.DailyWindowStart.Equal(resetAt), "expected daily window start updated")

	affected, err := s.repo.BatchUpdateExpiredStatus(s.ctx)
	s.Require().NoError(err, "BatchUpdateExpiredStatus")
	s.Require().Equal(int64(1), affected, "expected 1 affected row")
	updated, err := s.repo.GetByID(s.ctx, expiredActive.ID)
	s.Require().NoError(err, "GetByID expired")
	s.Require().Equal(service.SubscriptionStatusExpired, updated.Status, "expected status expired")
}
