package scout

import (
	"context"
	"errors"
	"testing"

	"apps/backend/internal/middleware"
	"apps/backend/internal/module/scout/domain"
	scoutRepo "apps/backend/internal/module/scout/repository"
	scoutUC "apps/backend/internal/module/scout/usecase"
	"apps/backend/internal/svc"
	"apps/backend/internal/types"
)

func testRun(id string, owner int64, created int64, status string) *domain.Run {
	r := domain.NewRun(id, "job-"+id, owner, domain.RunBrief{
		Intent: "敏感肌保養", Mode: domain.ModeTheme, ThemeKey: "skin", ThemeLabel: "敏感肌保養",
	}, created)
	r.Status = status
	if status == domain.RunSucceeded {
		r.EligibleCount = 1
	}
	return r
}

func testScoutContext(uid int64, repo *scoutRepo.MemoryStore) (*svc.ServiceContext, context.Context) {
	service := scoutUC.New(repo)
	return &svc.ServiceContext{Scout: service}, middleware.WithUID(context.Background(), uid)
}

func TestRunListLogicOwnerScopedPagination(t *testing.T) {
	repo := scoutRepo.NewMemory()
	for _, run := range []*domain.Run{
		testRun("run-old", 7, 100, domain.RunSucceeded),
		testRun("run-new", 7, 200, domain.RunSucceeded),
		testRun("run-other", 8, 300, domain.RunSucceeded),
	} {
		if err := repo.CreateRun(context.Background(), run); err != nil {
			t.Fatal(err)
		}
	}
	svcCtx, ctx := testScoutContext(7, repo)
	data, err := NewListScoutRunsLogic(ctx, svcCtx).ListScoutRuns(&types.ScoutRunListReq{Page: 1, PageSize: 1})
	if err != nil {
		t.Fatal(err)
	}
	if data.Pagination.Total != 2 || data.Pagination.TotalPages != 2 || len(data.List) != 1 || data.List[0].Id != "run-new" {
		t.Fatalf("unexpected run page: %+v", data)
	}
	if data.List[0].JobId == "" {
		t.Fatal("run response omitted job id")
	}
}

func TestRunPostsLogicReturnsRunAndIndependentPage(t *testing.T) {
	repo := scoutRepo.NewMemory()
	run := testRun("run-posts", 7, 100, domain.RunSucceeded)
	if err := repo.CreateRun(context.Background(), run); err != nil {
		t.Fatal(err)
	}
	for _, post := range []*domain.Post{
		{ID: "post-old", RunID: run.ID, OwnerUID: 7, Text: "old", PostedAt: 100, CreatedAt: 100},
		{ID: "post-new", RunID: run.ID, OwnerUID: 7, Text: "new", PostedAt: 200, CreatedAt: 200},
	} {
		if err := repo.PublishRunPosts(context.Background(), 7, run.ID, []*domain.Post{post}); err != nil {
			t.Fatal(err)
		}
	}
	svcCtx, ctx := testScoutContext(7, repo)
	data, err := NewListScoutRunPostsLogic(ctx, svcCtx).ListScoutRunPosts(&types.ScoutRunPostsReq{RunId: run.ID, Page: 2, PageSize: 1})
	if err != nil {
		t.Fatal(err)
	}
	if data.Run.Id != run.ID || data.Pagination.Total != 2 || data.Pagination.Page != 2 || len(data.List) != 1 || data.List[0].Id != "post-old" {
		t.Fatalf("unexpected run posts page: %+v", data)
	}
}

func TestRunRemoveLogicGuardsNonTerminalAndOwner(t *testing.T) {
	repo := scoutRepo.NewMemory()
	queued := testRun("run-queued", 7, 100, domain.RunQueued)
	done := testRun("run-done", 7, 200, domain.RunSucceeded)
	for _, run := range []*domain.Run{queued, done} {
		if err := repo.CreateRun(context.Background(), run); err != nil {
			t.Fatal(err)
		}
	}
	svcCtx, ctx := testScoutContext(7, repo)
	if _, err := NewRemoveScoutRunLogic(ctx, svcCtx).RemoveScoutRun(&types.ScoutRunPostsReq{RunId: queued.ID}); !errors.Is(err, domain.ErrIllegalRunStatus) {
		t.Fatalf("queued run removal error = %v, want illegal status", err)
	}
	otherCtx := middleware.WithUID(context.Background(), 8)
	if _, err := NewRemoveScoutRunLogic(otherCtx, svcCtx).RemoveScoutRun(&types.ScoutRunPostsReq{RunId: done.ID}); !errors.Is(err, domain.ErrNotFound) {
		t.Fatalf("cross-owner removal error = %v, want not found", err)
	}
	if data, err := NewRemoveScoutRunLogic(ctx, svcCtx).RemoveScoutRun(&types.ScoutRunPostsReq{RunId: done.ID}); err != nil || !data.Ok {
		t.Fatalf("terminal run removal = %+v, %v", data, err)
	}
	if _, err := repo.GetRun(context.Background(), 7, done.ID); !errors.Is(err, domain.ErrNotFound) {
		t.Fatalf("removed run still exists: %v", err)
	}
}

func TestRunLogicRequiresAuth(t *testing.T) {
	svcCtx := &svc.ServiceContext{Scout: scoutUC.New(scoutRepo.NewMemory())}
	_, err := NewListScoutRunsLogic(context.Background(), svcCtx).ListScoutRuns(&types.ScoutRunListReq{})
	if err == nil || err.Error() != "missing authorization" {
		t.Fatalf("auth error = %v", err)
	}
}
