package state

import (
	"fmt"
	"os"
	"sync"
	"testing"

	"github.com/voocel/ainovel-cli/domain"
)

func TestSaveAndLoadRunMeta(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	meta := domain.RunMeta{
		StartedAt: "2026-03-07T10:00:00+08:00",
		Provider:  "openrouter",
		Style:     "fantasy",
		Model:     "gpt-4o",
	}
	if err := store.SaveRunMeta(meta); err != nil {
		t.Fatalf("SaveRunMeta: %v", err)
	}

	loaded, err := store.LoadRunMeta()
	if err != nil {
		t.Fatalf("LoadRunMeta: %v", err)
	}
	if loaded.Style != "fantasy" {
		t.Errorf("style mismatch: %s", loaded.Style)
	}
	if loaded.Provider != "openrouter" {
		t.Errorf("provider mismatch: %s", loaded.Provider)
	}
	if loaded.Model != "gpt-4o" {
		t.Errorf("model mismatch: %s", loaded.Model)
	}
}

func TestLoadRunMeta_Empty(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	meta, err := store.LoadRunMeta()
	if err != nil {
		t.Fatalf("LoadRunMeta on empty: %v", err)
	}
	if meta != nil {
		t.Fatalf("expected nil, got %+v", meta)
	}
}

func TestAppendSteerEntry(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	// 首次追加（meta/run.json 不存在）
	e1 := domain.SteerEntry{Input: "主角改成女性", Timestamp: "2026-03-07T10:01:00+08:00"}
	if err := store.AppendSteerEntry(e1); err != nil {
		t.Fatalf("AppendSteerEntry 1: %v", err)
	}

	meta, _ := store.LoadRunMeta()
	if len(meta.SteerHistory) != 1 {
		t.Fatalf("expected 1 entry, got %d", len(meta.SteerHistory))
	}
	if meta.SteerHistory[0].Input != "主角改成女性" {
		t.Errorf("input mismatch: %s", meta.SteerHistory[0].Input)
	}

	// 追加第二条
	e2 := domain.SteerEntry{Input: "加入反转", Timestamp: "2026-03-07T10:02:00+08:00"}
	_ = store.AppendSteerEntry(e2)

	meta, _ = store.LoadRunMeta()
	if len(meta.SteerHistory) != 2 {
		t.Fatalf("expected 2 entries, got %d", len(meta.SteerHistory))
	}
}

func TestAppendSteerEntryConcurrent(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	const workers = 32
	var wg sync.WaitGroup
	start := make(chan struct{})

	for i := 0; i < workers; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			<-start
			entry := domain.SteerEntry{
				Input:     fmt.Sprintf("steer-%02d", i),
				Timestamp: fmt.Sprintf("ts-%02d", i),
			}
			if err := store.AppendSteerEntry(entry); err != nil {
				t.Errorf("AppendSteerEntry(%d): %v", i, err)
			}
		}(i)
	}

	close(start)
	wg.Wait()

	meta, err := store.LoadRunMeta()
	if err != nil {
		t.Fatalf("LoadRunMeta: %v", err)
	}
	if meta == nil {
		t.Fatal("expected run meta to exist")
	}
	if len(meta.SteerHistory) != workers {
		t.Fatalf("expected %d steer entries, got %d", workers, len(meta.SteerHistory))
	}

	seen := make(map[string]struct{}, workers)
	for _, entry := range meta.SteerHistory {
		seen[entry.Input] = struct{}{}
	}
	if len(seen) != workers {
		t.Fatalf("expected %d unique steer entries, got %d", workers, len(seen))
	}
}

func TestAppendSteerEntry_PreservesExistingMeta(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	// 先保存 RunMeta
	_ = store.SaveRunMeta(domain.RunMeta{
		StartedAt: "2026-03-07T10:00:00+08:00",
		Provider:  "openrouter",
		Style:     "suspense",
		Model:     "gpt-4o",
	})

	// 追加 Steer 不应覆盖其他字段
	_ = store.AppendSteerEntry(domain.SteerEntry{Input: "test", Timestamp: "now"})

	meta, _ := store.LoadRunMeta()
	if meta.Style != "suspense" {
		t.Errorf("style should be preserved, got %s", meta.Style)
	}
	if meta.Provider != "openrouter" {
		t.Errorf("provider should be preserved, got %s", meta.Provider)
	}
	if meta.Model != "gpt-4o" {
		t.Errorf("model should be preserved, got %s", meta.Model)
	}
	if len(meta.SteerHistory) != 1 {
		t.Errorf("expected 1 steer entry, got %d", len(meta.SteerHistory))
	}
}

func TestInitRunMeta_PreservesHistory(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	// 先建立带历史的 RunMeta
	_ = store.SaveRunMeta(domain.RunMeta{
		StartedAt:    "old",
		Provider:     "openai",
		Style:        "fantasy",
		Model:        "old-model",
		SteerHistory: []domain.SteerEntry{{Input: "历史干预", Timestamp: "ts"}},
		PendingSteer: "待处理",
	})

	// InitRunMeta 应保留 SteerHistory 和 PendingSteer
	_ = store.InitRunMeta("suspense", "openrouter", "new-model")

	meta, _ := store.LoadRunMeta()
	if meta.Style != "suspense" {
		t.Errorf("style should be updated, got %s", meta.Style)
	}
	if meta.Provider != "openrouter" {
		t.Errorf("provider should be updated, got %s", meta.Provider)
	}
	if meta.Model != "new-model" {
		t.Errorf("model should be updated, got %s", meta.Model)
	}
	if len(meta.SteerHistory) != 1 || meta.SteerHistory[0].Input != "历史干预" {
		t.Errorf("steer history should be preserved, got %v", meta.SteerHistory)
	}
	if meta.PendingSteer != "待处理" {
		t.Errorf("pending steer should be preserved, got %s", meta.PendingSteer)
	}
}

func TestSetAndClearPendingSteer(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	// 设置 PendingSteer
	if err := store.SetPendingSteer("主角改成女性"); err != nil {
		t.Fatalf("SetPendingSteer: %v", err)
	}
	meta, _ := store.LoadRunMeta()
	if meta.PendingSteer != "主角改成女性" {
		t.Errorf("expected pending steer, got %s", meta.PendingSteer)
	}

	// 清除
	if err := store.ClearPendingSteer(); err != nil {
		t.Fatalf("ClearPendingSteer: %v", err)
	}
	meta, _ = store.LoadRunMeta()
	if meta.PendingSteer != "" {
		t.Errorf("expected empty pending steer, got %s", meta.PendingSteer)
	}
}

func TestSetPlanningTier(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	if err := store.SetPlanningTier(domain.PlanningTierLong); err != nil {
		t.Fatalf("SetPlanningTier: %v", err)
	}

	meta, err := store.LoadRunMeta()
	if err != nil {
		t.Fatalf("LoadRunMeta: %v", err)
	}
	if meta == nil {
		t.Fatal("expected run meta to exist")
	}
	if meta.PlanningTier != domain.PlanningTierLong {
		t.Fatalf("expected planning tier %q, got %q", domain.PlanningTierLong, meta.PlanningTier)
	}
}

func TestClearPendingSteer_Noop(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)

	// 空 meta 上调用不报错
	if err := store.ClearPendingSteer(); err != nil {
		t.Fatalf("ClearPendingSteer on empty: %v", err)
	}
}

func TestSaveCheckpoint(t *testing.T) {
	dir := t.TempDir()
	store := NewStore(dir)
	_ = store.InitProgress("test", 10)

	if err := store.SaveCheckpoint("ch01-commit"); err != nil {
		t.Fatalf("SaveCheckpoint: %v", err)
	}

	// 验证 checkpoint 目录下有文件
	entries, err := os.ReadDir(dir + "/meta/checkpoints")
	if err != nil {
		t.Fatalf("read checkpoints dir: %v", err)
	}
	if len(entries) != 1 {
		t.Fatalf("expected 1 checkpoint, got %d", len(entries))
	}
}
