package cpweeklyrank_test

import (
	"context"
	"errors"
	"strconv"
	"testing"
	"time"

	"hyapp/pkg/appcode"
	domain "hyapp/services/activity-service/internal/domain/cpweeklyrank"
	service "hyapp/services/activity-service/internal/service/cpweeklyrank"
)

func TestGetStatusReturnsPreviousEntriesFromPreviousWeek(t *testing.T) {
	now := time.Date(2026, 6, 24, 12, 0, 0, 0, time.UTC)
	currentStart, currentEnd := service.WeekWindow(now)
	previousStart, previousEnd := service.PreviousWeekWindow(now)
	rankSource := &fakeCPWeeklyRankSource{
		windows: map[[2]int64][]domain.Entry{
			{currentStart.UnixMilli(), currentEnd.UnixMilli()}:   cpWeeklyRankEntries(2, 1000),
			{previousStart.UnixMilli(), previousEnd.UnixMilli()}: cpWeeklyRankEntries(5, 5000),
		},
	}
	svc := service.New(&fakeCPWeeklyRankRepository{config: domain.Config{
		AppCode:      "lalu",
		ActivityCode: domain.ActivityCode,
		Enabled:      true,
		RelationType: "cp",
		TopCount:     10,
	}}, rankSource, nil)
	svc.SetClock(func() time.Time { return now })

	status, err := svc.GetStatus(appcode.WithContext(context.Background(), "lalu"), 1, 0)
	if err != nil {
		t.Fatalf("GetStatus failed: %v", err)
	}
	if len(status.Entries) != 1 {
		t.Fatalf("current entries mismatch: %+v", status.Entries)
	}
	if len(status.PreviousEntries) != 3 {
		t.Fatalf("previous entries should be limited to 3, got %+v", status.PreviousEntries)
	}
	if status.PreviousEntries[0].UserA.Username != "left-1" || status.PreviousEntries[0].UserA.Avatar != "https://avatar/left-1.png" ||
		status.PreviousEntries[0].UserB.Username != "right-1" || status.PreviousEntries[0].UserB.Avatar != "https://avatar/right-1.png" ||
		status.PreviousEntries[0].Score != 5001 {
		t.Fatalf("previous entry should keep user snapshots and intimacy score: %+v", status.PreviousEntries[0])
	}
	if len(rankSource.calls) != 2 {
		t.Fatalf("rank source call count mismatch: %+v", rankSource.calls)
	}
	if rankSource.calls[0].startMS != currentStart.UnixMilli() || rankSource.calls[0].endMS != currentEnd.UnixMilli() || rankSource.calls[0].limit != 1 {
		t.Fatalf("current window call mismatch: %+v", rankSource.calls[0])
	}
	if rankSource.calls[1].startMS != previousStart.UnixMilli() || rankSource.calls[1].endMS != previousEnd.UnixMilli() || rankSource.calls[1].limit != 3 {
		t.Fatalf("previous window call mismatch: %+v", rankSource.calls[1])
	}
}

type fakeCPWeeklyRankRepository struct {
	config domain.Config
}

func (r *fakeCPWeeklyRankRepository) GetCPWeeklyRankConfig(context.Context) (domain.Config, bool, error) {
	return r.config, true, nil
}

func (r *fakeCPWeeklyRankRepository) UpdateCPWeeklyRankConfig(context.Context, domain.Config, int64) (domain.Config, error) {
	return domain.Config{}, errors.New("not implemented")
}

func (r *fakeCPWeeklyRankRepository) ListCPWeeklyRankSettlements(context.Context, int64, int64) ([]domain.Settlement, error) {
	return nil, errors.New("not implemented")
}

func (r *fakeCPWeeklyRankRepository) PrepareCPWeeklyRankSettlements(context.Context, domain.Config, []domain.Entry, int64, int64, int64) ([]domain.Settlement, error) {
	return nil, errors.New("not implemented")
}

func (r *fakeCPWeeklyRankRepository) ListPendingCPWeeklyRankSettlements(context.Context, int64, int64, int) ([]domain.Settlement, error) {
	return nil, errors.New("not implemented")
}

func (r *fakeCPWeeklyRankRepository) MarkCPWeeklyRankSettlementGranted(context.Context, string, string, int64) error {
	return errors.New("not implemented")
}

func (r *fakeCPWeeklyRankRepository) MarkCPWeeklyRankSettlementFailed(context.Context, string, string, int64) error {
	return errors.New("not implemented")
}

type fakeCPWeeklyRankSource struct {
	calls   []fakeCPWeeklyRankSourceCall
	windows map[[2]int64][]domain.Entry
}

type fakeCPWeeklyRankSourceCall struct {
	relationType string
	startMS      int64
	endMS        int64
	limit        int32
}

func (s *fakeCPWeeklyRankSource) ListCPWeeklyRankEntries(_ context.Context, relationType string, startMS int64, endMS int64, limit int32) ([]domain.Entry, error) {
	s.calls = append(s.calls, fakeCPWeeklyRankSourceCall{
		relationType: relationType,
		startMS:      startMS,
		endMS:        endMS,
		limit:        limit,
	})
	entries := append([]domain.Entry(nil), s.windows[[2]int64{startMS, endMS}]...)
	if limit > 0 && len(entries) > int(limit) {
		entries = entries[:limit]
	}
	return entries, nil
}

func cpWeeklyRankEntries(count int, baseScore int64) []domain.Entry {
	entries := make([]domain.Entry, 0, count)
	for index := 1; index <= count; index++ {
		suffix := strconv.Itoa(index)
		entries = append(entries, domain.Entry{
			Rank:           int64(index),
			RelationshipID: "cp-rel-" + suffix,
			RelationType:   "cp",
			Score:          baseScore + int64(index),
			UserA: domain.User{
				UserID:        int64(1000 + index),
				DisplayUserID: "L" + suffix,
				Username:      "left-" + suffix,
				Avatar:        "https://avatar/left-" + suffix + ".png",
			},
			UserB: domain.User{
				UserID:        int64(2000 + index),
				DisplayUserID: "R" + suffix,
				Username:      "right-" + suffix,
				Avatar:        "https://avatar/right-" + suffix + ".png",
			},
		})
	}
	return entries
}
