package topdata

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestBuildNativeGeneratesAndSkipsWikiPages(t *testing.T) {
	root := testProjectRoot(t)
	mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
	writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{
  "output": "skills.2da",
  "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
  "rows": [
    {
      "id": 0,
      "key": "skills:athletics",
      "Label": "Athletics",
      "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
      "Description": {"tlk": {"key": "skills:athletics.description", "text": "Ability: Strength.\n\nUses:\n- Climb\n- Swim"}},
      "HideFromLevelUp": "0",
      "Untrained": "1",
      "KeyAbility": "STR",
      "ArmorCheckPenalty": "1",
      "Constant": "SKILL_ATHLETICS"
    }
  ]
}`+"\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n")

	proj := testProject(root)
	proj.Config.TopData.ReferenceBuilder = ""

	result, err := BuildNative(proj, nil)
	if err != nil {
		t.Fatalf("BuildNative failed: %v", err)
	}
	if result.WikiPages != 1 {
		t.Fatalf("expected one generated entity page, got %d", result.WikiPages)
	}
	pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "athletics.txt")
	got, err := os.ReadFile(pagePath)
	if err != nil {
		t.Fatalf("read generated page: %v", err)
	}
	text := string(got)
	if !strings.Contains(text, "====== Athletics ======") || !strings.Contains(text, "This skill is unchanged from vanilla.") {
		t.Fatalf("unexpected generated page:\n%s", text)
	}
	if !strings.Contains(text, "Ability: Strength.") || !strings.Contains(text, "  * Climb") {
		t.Fatalf("expected description to be rendered into wiki page:\n%s", text)
	}
	statusPath := filepath.Join(root, ".cache", "wiki", "pages", "meta", "wikistatus.txt")
	statusRaw, err := os.ReadFile(statusPath)
	if err != nil {
		t.Fatalf("read status page: %v", err)
	}
	if !strings.Contains(string(statusRaw), "**Vanilla:** 1\\\\") {
		t.Fatalf("expected wiki status summary to count vanilla page:\n%s", string(statusRaw))
	}
	if _, err := os.Stat(filepath.Join(root, ".cache", "wiki", "state.json")); err != nil {
		t.Fatalf("expected state file after first build: %v", err)
	}
	stateBefore, err := loadWikiState(filepath.Join(root, ".cache", "wiki", "state.json"))
	if err != nil {
		t.Fatalf("read state before second build: %v", err)
	}
	digestBefore, err := computeWikiSourceDigest(filepath.Join(root, "topdata"))
	if err != nil {
		t.Fatalf("compute digest before second build: %v", err)
	}
	if stateBefore.Digest != digestBefore {
		t.Fatalf("expected state digest to match current digest before second build, got state=%s current=%s", stateBefore.Digest, digestBefore)
	}

	second, err := BuildNative(proj, nil)
	if err != nil {
		t.Fatalf("second BuildNative failed: %v", err)
	}
	if second.WikiPages != 1 {
		t.Fatalf("expected a single generated skill wiki page after rebuild, got %d", second.WikiPages)
	}
}

func TestBuildAndPackageBuildsWikiOnlyWhenRequested(t *testing.T) {
	root := testProjectRoot(t)
	mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
	writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{
  "output": "skills.2da",
  "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
  "rows": [
    {
      "id": 0,
      "key": "skills:athletics",
      "Label": "Athletics",
      "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
      "Description": {"tlk": {"key": "skills:athletics.description", "text": "Skill text."}},
      "HideFromLevelUp": "0",
      "Untrained": "1",
      "KeyAbility": "STR",
      "ArmorCheckPenalty": "1",
      "Constant": "SKILL_ATHLETICS"
    }
  ]
}`+"\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n")

	proj := testProject(root)
	proj.Config.TopData.ReferenceBuilder = ""

	result, err := BuildAndPackageWithOptions(proj, BuildAndPackageOptions{}, nil)
	if err != nil {
		t.Fatalf("BuildAndPackageWithOptions failed: %v", err)
	}
	if result.WikiOutputDir != "" || result.WikiPages != 0 {
		t.Fatalf("expected wiki generation to be skipped by default, got dir=%q pages=%d", result.WikiOutputDir, result.WikiPages)
	}
	if _, err := os.Stat(filepath.Join(root, ".cache", "wiki", "state.json")); !os.IsNotExist(err) {
		t.Fatalf("expected no wiki state without explicit wiki build, got %v", err)
	}

	result, err = BuildAndPackageWithOptions(proj, BuildAndPackageOptions{Force: true, BuildWiki: true}, nil)
	if err != nil {
		t.Fatalf("BuildAndPackageWithOptions with wiki failed: %v", err)
	}
	if result.WikiPages != 1 {
		t.Fatalf("expected one generated wiki page when requested, got %d", result.WikiPages)
	}
	if _, err := os.Stat(filepath.Join(root, ".cache", "wiki", "state.json")); err != nil {
		t.Fatalf("expected wiki state after explicit wiki build: %v", err)
	}
}

func TestBuildNativeRegeneratesWikiWhenTLKTextChanges(t *testing.T) {
	root := testProjectRoot(t)
	mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
	writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
	basePath := filepath.Join(root, "topdata", "data", "skills", "base.json")
	writeFile(t, basePath, `{
  "output": "skills.2da",
  "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
  "rows": [
    {
      "id": 0,
      "key": "skills:athletics",
      "Label": "Athletics",
      "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
      "Description": {"tlk": {"key": "skills:athletics.description", "text": "Original text."}},
      "HideFromLevelUp": "0",
      "Untrained": "1",
      "KeyAbility": "STR",
      "ArmorCheckPenalty": "1",
      "Constant": "SKILL_ATHLETICS"
    }
  ]
}`+"\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n")

	proj := testProject(root)
	proj.Config.TopData.ReferenceBuilder = ""

	if _, err := BuildNative(proj, nil); err != nil {
		t.Fatalf("initial BuildNative failed: %v", err)
	}
	writeFile(t, basePath, `{
  "output": "skills.2da",
  "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
  "rows": [
    {
      "id": 0,
      "key": "skills:athletics",
      "Label": "Athletics",
      "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
      "Description": {"tlk": {"key": "skills:athletics.description", "text": "Updated text."}},
      "HideFromLevelUp": "0",
      "Untrained": "1",
      "KeyAbility": "STR",
      "ArmorCheckPenalty": "1",
      "Constant": "SKILL_ATHLETICS"
    }
  ]
}`+"\n")

	if _, err := BuildNative(proj, nil); err != nil {
		t.Fatalf("BuildNative after text change failed: %v", err)
	}
	pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "athletics.txt")
	got, err := os.ReadFile(pagePath)
	if err != nil {
		t.Fatalf("read regenerated page: %v", err)
	}
	if !strings.Contains(string(got), "Updated text.") {
		t.Fatalf("expected regenerated page to contain updated description:\n%s", string(got))
	}
}

func TestBuildPackageIgnoresWikiSourcesForFreshness(t *testing.T) {
	root := testProjectRoot(t)
	mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
	writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{
  "output": "skills.2da",
  "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
  "rows": [
    {
      "id": 0,
      "key": "skills:athletics",
      "Label": "Athletics",
      "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
      "Description": {"tlk": {"key": "skills:athletics.description", "text": "Skill text."}},
      "HideFromLevelUp": "0",
      "Untrained": "1",
      "KeyAbility": "STR",
      "ArmorCheckPenalty": "1",
      "Constant": "SKILL_ATHLETICS"
    }
  ]
}`+"\n")
	writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n")

	proj := testProject(root)
	proj.Config.TopData.ReferenceBuilder = ""

	if _, err := BuildNative(proj, nil); err != nil {
		t.Fatalf("BuildNative failed: %v", err)
	}

	pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "athletics.txt")
	newTime := time.Now().Add(2 * time.Second)
	if err := os.Chtimes(pagePath, newTime, newTime); err != nil {
		t.Fatalf("touch wiki page: %v", err)
	}

	if _, err := BuildPackage(proj, nil); err != nil {
		t.Fatalf("expected wiki output to be ignored for freshness, got %v", err)
	}
}
