package main

import (
	"encoding/json"
	"os"
	"path/filepath"
	"testing"
)

func TestResolveDataDirAbsolute(t *testing.T) {
	abs := filepath.Join(t.TempDir(), "data")
	got := resolveDataDir(abs, "C:\\project")
	if got != abs {
		t.Fatalf("absolute data dir unchanged: got %q want %q", got, abs)
	}
}

func TestResolveDataDirRelativeWithProjectRoot(t *testing.T) {
	root := filepath.Join(t.TempDir(), "repo")
	got := resolveDataDir("data", root)
	want := filepath.Join(root, "data")
	if got != want {
		t.Fatalf("got %q want %q", got, want)
	}
}

func TestResolveDataDirRelativeWithoutProjectRoot(t *testing.T) {
	got := resolveDataDir("data", "")
	if !filepath.IsAbs(got) {
		t.Fatalf("expected absolute path when project root empty, got %q", got)
	}
	if filepath.Base(got) != "data" {
		t.Fatalf("expected basename data, got %q", got)
	}
}

func TestResolveDataDirDotProjectRoot(t *testing.T) {
	got := resolveDataDir("data", ".")
	if !filepath.IsAbs(got) {
		t.Fatalf("expected absolute path, got %q", got)
	}
}

func TestFindProjectRootFromServerDir(t *testing.T) {
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	root := findProjectRoot()
	found := false
	for _, name := range []string{"devrun.bat", "LAUNCH.bat", "run.bat"} {
		if _, err := os.Stat(filepath.Join(root, name)); err == nil {
			found = true
			break
		}
	}
	if !found {
		t.Fatalf("findProjectRoot=%q missing devrun.bat/LAUNCH.bat/run.bat", root)
	}
	// When tests run from server/, root should be parent of cwd or cwd itself.
	if root != cwd && root != filepath.Dir(cwd) {
		t.Logf("findProjectRoot=%q cwd=%q (acceptable if marker layout differs)", root, cwd)
	}
}

func TestFindAgentSourceDir(t *testing.T) {
	dir := findAgentSourceDir()
	goMod := filepath.Join(dir, "go.mod")
	if _, err := os.Stat(goMod); err != nil {
		t.Fatalf("agent source %q missing go.mod: %v", dir, err)
	}
}

func TestFindWebRoot(t *testing.T) {
	dir := findWebRoot()
	if dir == "" {
		t.Fatal("findWebRoot returned empty string")
	}
	index := filepath.Join(dir, "index.html")
	if _, err := os.Stat(index); err != nil {
		t.Fatalf("web root %q missing index.html: %v", dir, err)
	}
}

func TestServerConfigProviderPublicURL(t *testing.T) {
	cfg := DefaultConfig()
	cfg.Server.PublicURL = "https://forge.example"
	p := &serverConfigProvider{config: cfg}
	if got := p.PublicURL(); got != "https://forge.example" {
		t.Fatalf("PublicURL: got %q", got)
	}
}

func TestServerConfigProviderGetConfigJSON(t *testing.T) {
	cfg := DefaultConfig()
	cfg.Port = 7777
	p := &serverConfigProvider{config: cfg}
	raw := p.GetConfigJSON()
	var decoded Config
	if err := json.Unmarshal(raw, &decoded); err != nil {
		t.Fatal(err)
	}
	if decoded.Port != 7777 {
		t.Fatalf("port in JSON: got %d", decoded.Port)
	}
}

func TestServerConfigProviderUpdateConfigFromJSON(t *testing.T) {
	dir := t.TempDir()
	cfg := DefaultConfig()
	cfg.DataDir = dir
	cfg.Wallet.Address = "48keep"
	p := &serverConfigProvider{config: cfg}

	if err := p.UpdateConfigFromJSON([]byte(`{"port":8888}`)); err != nil {
		t.Fatal(err)
	}
	if cfg.Port != 8888 {
		t.Fatalf("port not updated: %d", cfg.Port)
	}
	if cfg.Wallet.Address != "48keep" {
		t.Fatalf("partial update must preserve wallet: %q", cfg.Wallet.Address)
	}

	raw, err := os.ReadFile(filepath.Join(dir, "config.json"))
	if err != nil {
		t.Fatal(err)
	}
	var saved Config
	if err := json.Unmarshal(raw, &saved); err != nil {
		t.Fatal(err)
	}
	if saved.Port != 8888 {
		t.Fatalf("saved port: got %d", saved.Port)
	}
}

func TestServerConfigProviderUpdateConfigInvalidJSON(t *testing.T) {
	cfg := DefaultConfig()
	p := &serverConfigProvider{config: cfg}
	err := p.UpdateConfigFromJSON([]byte(`{not json`))
	if err == nil {
		t.Fatal("expected error for invalid JSON")
	}
}

func TestServerConfigProviderOnSavedCallback(t *testing.T) {
	dir := t.TempDir()
	cfg := DefaultConfig()
	cfg.DataDir = dir
	called := false
	p := &serverConfigProvider{
		config: cfg,
		onSaved: func(c *Config) {
			called = true
			if c.Port != 6666 {
				t.Fatalf("callback port: got %d", c.Port)
			}
		},
	}
	if err := p.UpdateConfigFromJSON([]byte(`{"port":6666}`)); err != nil {
		t.Fatal(err)
	}
	if !called {
		t.Fatal("onSaved callback not invoked")
	}
}
