package usecase

import (
	"context"
	"testing"
	"time"

	"apps/backend/internal/module/crm/domain"
	"apps/backend/internal/module/crm/repository"
)

const day = int64(24 * time.Hour)

func seedFollowUp(t *testing.T, repo domain.Repository, days int) (*domain.Contact, *domain.FollowUp) {
	t.Helper()
	ctx := context.Background()
	c, err := repo.UpsertContactByIdentity(ctx, &domain.Contact{
		OwnerUID: 7, AuthorHandle: "buyer", FollowUpDays: days,
	})
	if err != nil {
		t.Fatal(err)
	}
	f := &domain.FollowUp{
		ID: domain.NewID(), OwnerUID: 7, ContactID: c.ID,
		DueAt: 1, Status: domain.FollowUpScheduled,
		CreatedAt: 1, UpdatedAt: 1,
	}
	if err := repo.SaveFollowUp(ctx, f); err != nil {
		t.Fatal(err)
	}
	return c, f
}

// FU-01／FU-03：第一次通知後要再等一個間隔才第二次通知，第二次之後才 escalated。
func TestScanFollowUpsNotifiesTwiceThenEscalates(t *testing.T) {
	ctx := context.Background()
	repo := repository.NewMemory()
	svc := New(repo)
	_, f := seedFollowUp(t, repo, 3)

	now := int64(10 * day)
	if n, err := svc.ScanFollowUps(ctx, now); err != nil || n != 1 {
		t.Fatalf("first scan n=%d err=%v", n, err)
	}
	first, err := repo.GetFollowUp(ctx, f.ID)
	if err != nil || first.Status != domain.FollowUpNotified || first.NotifiedCount != 1 {
		t.Fatalf("after first scan=%+v err=%v", first, err)
	}
	if first.DueAt != now+3*day {
		t.Fatalf("first notify must push due_at by the contact interval, got %d", first.DueAt)
	}

	// 還沒到下一次到期：不可重複通知
	if n, err := svc.ScanFollowUps(ctx, now+day); err != nil || n != 0 {
		t.Fatalf("premature rescan n=%d err=%v", n, err)
	}

	if n, err := svc.ScanFollowUps(ctx, now+3*day); err != nil || n != 1 {
		t.Fatalf("second scan n=%d err=%v", n, err)
	}
	second, err := repo.GetFollowUp(ctx, f.ID)
	if err != nil || second.Status != domain.FollowUpEscalated || second.NotifiedCount != 2 {
		t.Fatalf("after second scan=%+v err=%v", second, err)
	}

	// escalated 是終點：不再被掃到
	if n, err := svc.ScanFollowUps(ctx, now+30*day); err != nil || n != 0 {
		t.Fatalf("escalated must stop scanning n=%d err=%v", n, err)
	}
}

// FU-04：延後把到期日後移並回到 scheduled。
func TestSnoozeReturnsToScheduled(t *testing.T) {
	ctx := context.Background()
	repo := repository.NewMemory()
	svc := New(repo)
	_, f := seedFollowUp(t, repo, 3)
	if _, err := svc.ScanFollowUps(ctx, 10*day); err != nil {
		t.Fatal(err)
	}

	before := domain.NowNano()
	got, err := svc.SnoozeFollowUp(ctx, 7, f.ID, 5)
	if err != nil {
		t.Fatal(err)
	}
	if got.Status != domain.FollowUpScheduled {
		t.Fatalf("snooze status = %s, want scheduled", got.Status)
	}
	if got.DueAt < before+5*day {
		t.Fatalf("snooze must push due_at at least 5 days out, got %d", got.DueAt)
	}
}

func TestScanFollowUpsFallsBackToDefaultInterval(t *testing.T) {
	ctx := context.Background()
	repo := repository.NewMemory()
	svc := New(repo)
	f := &domain.FollowUp{
		ID: domain.NewID(), OwnerUID: 7, ContactID: "missing-contact",
		DueAt: 1, Status: domain.FollowUpScheduled, CreatedAt: 1, UpdatedAt: 1,
	}
	if err := repo.SaveFollowUp(ctx, f); err != nil {
		t.Fatal(err)
	}
	now := int64(10 * day)
	if _, err := svc.ScanFollowUps(ctx, now); err != nil {
		t.Fatal(err)
	}
	got, err := repo.GetFollowUp(ctx, f.ID)
	if err != nil || got.DueAt != now+int64(domain.DefaultFollowUpDays)*day {
		t.Fatalf("missing contact should use default interval, got %+v err=%v", got, err)
	}
}
