package doctor

import (
	"errors"
	"testing"

	"github.com/steveyegge/gastown/internal/config"
)

// mockEnvReader implements SessionEnvReader for testing.
type mockEnvReader struct {
	sessions    []string
	sessionEnvs map[string]map[string]string
	listErr     error
	envErrs     map[string]error
}

func (m *mockEnvReader) ListSessions() ([]string, error) {
	if m.listErr != nil {
		return nil, m.listErr
	}
	return m.sessions, nil
}

func (m *mockEnvReader) GetAllEnvironment(session string) (map[string]string, error) {
	if m.envErrs != nil {
		if err, ok := m.envErrs[session]; ok {
			return nil, err
		}
	}
	if m.sessionEnvs != nil {
		if env, ok := m.sessionEnvs[session]; ok {
			return env, nil
		}
	}
	return map[string]string{}, nil
}

// testTownRoot is the town root used in tests.
// Tests use this fixed path so expected values match what the check generates.
const testTownRoot = "/town"

// expectedEnv generates expected env vars matching what the check generates.
// For town-level roles (mayor, deacon), beadsDir is /town/.beads
// For rig-level roles, beadsDir is /town/rigName/.beads
func expectedEnv(role, rig, agentName string) map[string]string {
	var beadsDir string
	if rig != "" {
		beadsDir = testTownRoot + "/" + rig + "/.beads"
	} else {
		beadsDir = testTownRoot + "/.beads"
	}
	return config.AgentEnv(config.AgentEnvConfig{
		Role:      role,
		Rig:       rig,
		AgentName: agentName,
		TownRoot:  testTownRoot,
		BeadsDir:  beadsDir,
	})
}

// testCtx returns a CheckContext with the test town root.
func testCtx() *CheckContext {
	return &CheckContext{TownRoot: testTownRoot}
}

func TestEnvVarsCheck_NoSessions(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
	if result.Message != "No Gas Town sessions running" {
		t.Errorf("Message = %q, want %q", result.Message, "No Gas Town sessions running")
	}
}

func TestEnvVarsCheck_ListSessionsError(t *testing.T) {
	reader := &mockEnvReader{
		listErr: errors.New("tmux not running"),
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	// No tmux server is valid (Gas Town can be down)
	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
	if result.Message != "No tmux sessions running" {
		t.Errorf("Message = %q, want %q", result.Message, "No tmux sessions running")
	}
}

func TestEnvVarsCheck_NonGasTownSessions(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{"other-session", "my-dev"},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
	if result.Message != "No Gas Town sessions running" {
		t.Errorf("Message = %q, want %q", result.Message, "No Gas Town sessions running")
	}
}

func TestEnvVarsCheck_MayorCorrect(t *testing.T) {
	expected := expectedEnv("mayor", "", "")
	reader := &mockEnvReader{
		sessions: []string{"hq-mayor"},
		sessionEnvs: map[string]map[string]string{
			"hq-mayor": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}

func TestEnvVarsCheck_MayorMissing(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{"hq-mayor"},
		sessionEnvs: map[string]map[string]string{
			"hq-mayor": {}, // Missing all env vars
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusWarning {
		t.Errorf("Status = %v, want StatusWarning", result.Status)
	}
}

func TestEnvVarsCheck_WitnessCorrect(t *testing.T) {
	expected := expectedEnv("witness", "myrig", "")
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-witness"},
		sessionEnvs: map[string]map[string]string{
			"gt-myrig-witness": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}

func TestEnvVarsCheck_WitnessMismatch(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-witness"},
		sessionEnvs: map[string]map[string]string{
			"gt-myrig-witness": {
				"GT_ROLE": "witness",
				"GT_RIG":  "wrongrig", // Wrong rig
			},
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusWarning {
		t.Errorf("Status = %v, want StatusWarning", result.Status)
	}
}

func TestEnvVarsCheck_RefineryCorrect(t *testing.T) {
	expected := expectedEnv("refinery", "myrig", "")
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-refinery"},
		sessionEnvs: map[string]map[string]string{
			"gt-myrig-refinery": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}

func TestEnvVarsCheck_PolecatCorrect(t *testing.T) {
	expected := expectedEnv("polecat", "myrig", "Toast")
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-Toast"},
		sessionEnvs: map[string]map[string]string{
			"gt-myrig-Toast": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}

func TestEnvVarsCheck_PolecatMissing(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-Toast"},
		sessionEnvs: map[string]map[string]string{
			"gt-myrig-Toast": {
				"GT_ROLE": "polecat",
				// Missing GT_RIG, GT_POLECAT, BD_ACTOR, GIT_AUTHOR_NAME
			},
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusWarning {
		t.Errorf("Status = %v, want StatusWarning", result.Status)
	}
}

func TestEnvVarsCheck_CrewCorrect(t *testing.T) {
	expected := expectedEnv("crew", "myrig", "worker1")
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-crew-worker1"},
		sessionEnvs: map[string]map[string]string{
			"gt-myrig-crew-worker1": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}

func TestEnvVarsCheck_MultipleSessions(t *testing.T) {
	mayorEnv := expectedEnv("mayor", "", "")
	witnessEnv := expectedEnv("witness", "rig1", "")
	polecatEnv := expectedEnv("polecat", "rig1", "Toast")

	reader := &mockEnvReader{
		sessions: []string{"hq-mayor", "gt-rig1-witness", "gt-rig1-Toast"},
		sessionEnvs: map[string]map[string]string{
			"hq-mayor":        mayorEnv,
			"gt-rig1-witness": witnessEnv,
			"gt-rig1-Toast":   polecatEnv,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
	if result.Message != "All 3 session(s) have correct environment variables" {
		t.Errorf("Message = %q", result.Message)
	}
}

func TestEnvVarsCheck_MixedCorrectAndMismatch(t *testing.T) {
	mayorEnv := expectedEnv("mayor", "", "")

	reader := &mockEnvReader{
		sessions: []string{"hq-mayor", "gt-rig1-witness"},
		sessionEnvs: map[string]map[string]string{
			"hq-mayor": mayorEnv,
			"gt-rig1-witness": {
				"GT_ROLE": "witness",
				// Missing GT_RIG and other vars
			},
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusWarning {
		t.Errorf("Status = %v, want StatusWarning", result.Status)
	}
}

func TestEnvVarsCheck_DeaconCorrect(t *testing.T) {
	expected := expectedEnv("deacon", "", "")
	reader := &mockEnvReader{
		sessions: []string{"hq-deacon"},
		sessionEnvs: map[string]map[string]string{
			"hq-deacon": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}

func TestEnvVarsCheck_DeaconMissing(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{"hq-deacon"},
		sessionEnvs: map[string]map[string]string{
			"hq-deacon": {}, // Missing all env vars
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusWarning {
		t.Errorf("Status = %v, want StatusWarning", result.Status)
	}
}

func TestEnvVarsCheck_GetEnvError(t *testing.T) {
	reader := &mockEnvReader{
		sessions: []string{"gt-myrig-witness"},
		envErrs: map[string]error{
			"gt-myrig-witness": errors.New("session not found"),
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusWarning {
		t.Errorf("Status = %v, want StatusWarning", result.Status)
	}
}

func TestEnvVarsCheck_HyphenatedRig(t *testing.T) {
	// Test rig name with hyphens: "foo-bar"
	expected := expectedEnv("witness", "foo-bar", "")
	reader := &mockEnvReader{
		sessions: []string{"gt-foo-bar-witness"},
		sessionEnvs: map[string]map[string]string{
			"gt-foo-bar-witness": expected,
		},
	}
	check := NewEnvVarsCheckWithReader(reader)
	result := check.Run(testCtx())

	if result.Status != StatusOK {
		t.Errorf("Status = %v, want StatusOK", result.Status)
	}
}
