package org

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

func TestLoadHierarchicalV1(t *testing.T) {
	// Find repo root.
	wd, _ := os.Getwd()
	root := wd
	for i := 0; i < 6; i++ {
		if _, err := os.Stat(filepath.Join(root, ".td", "orgs", "hierarchical-v1.yaml")); err == nil {
			break
		}
		root = filepath.Dir(root)
	}
	path := filepath.Join(root, ".td", "orgs", "hierarchical-v1.yaml")
	d, err := Load(path)
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if d.Name != "hierarchical-v1" || d.Version != 1 {
		t.Errorf("name/version = %s/%d", d.Name, d.Version)
	}
	if d.RoleSet != "roles/v1/" {
		t.Errorf("role_set = %q", d.RoleSet)
	}
	if len(d.SubOrgs) != 2 {
		t.Errorf("sub_orgs = %d, want 2", len(d.SubOrgs))
	}
	if len(d.Roles) < 5 {
		t.Errorf("roles = %d, want >=5", len(d.Roles))
	}
	if d.Policies.MaxDepth != 4 || d.Policies.MaxFanout != 3 {
		t.Errorf("policies = %+v", d.Policies)
	}
	if d.Policies.MaxTokensPerRun != 2000000 {
		t.Errorf("max_tokens_per_run = %d", d.Policies.MaxTokensPerRun)
	}
	if !d.Policies.CrossSubOrgConn {
		t.Error("cross_suborg_requires_connector should be true")
	}
}

func TestValidateRejectsUnknownDelegate(t *testing.T) {
	bad := `name: bogus
version: 1
roles:
  - id: a
    delegates_to: [nonexistent]
`
	d, _ := parse(bad)
	if err := validate(d); err == nil {
		t.Error("expected validation to reject unknown delegate target")
	}
}

// TestValidateRejectsDelegationCycle is the V68 regression: an org with a
// delegation cycle (a → b → a, or self-loop a → a) must be rejected at load
// time. Without this the harness would spawn both agents and route messages
// in an infinite loop at runtime.
func TestValidateRejectsDelegationCycle(t *testing.T) {
	cycles := []struct {
		name, yaml string
	}{
		{"self-loop", `name: x
version: 1
roles:
  - id: a
    delegates_to: [a]
`},
		{"two-cycle", `name: x
version: 1
roles:
  - id: a
    delegates_to: [b]
  - id: b
    delegates_to: [a]
`},
		{"three-cycle", `name: x
version: 1
roles:
  - id: master
    delegates_to: [orchestrator]
  - id: orchestrator
    delegates_to: [lead]
  - id: lead
    delegates_to: [master]
`},
	}
	for _, c := range cycles {
		t.Run(c.name, func(t *testing.T) {
			d, _ := parse(c.yaml)
			if err := validate(d); err == nil {
				t.Errorf("validate accepted a cyclic org: %s", c.name)
			}
		})
	}

	// Sanity: a valid acyclic org passes.
	ok := `name: x
version: 1
roles:
  - id: master
    delegates_to: [worker]
  - id: worker
`
	d, _ := parse(ok)
	if err := validate(d); err != nil {
		t.Errorf("validate rejected a legitimate acyclic org: %v", err)
	}
}

// OC3: a role with system_prompt: should round-trip through the parser
// and ResolveSystemPromptPath should prefer the override over Definition.
func TestSystemPromptOverride(t *testing.T) {
	y := `name: x
version: 1
roles:
  - id: fe-lead
    definition: roles/v1/fe-lead.md
    system_prompt: overrides/fe-lead.md
    provider: codex
`
	d, err := parse(y)
	if err != nil {
		t.Fatal(err)
	}
	if len(d.Roles) != 1 {
		t.Fatalf("roles: %+v", d.Roles)
	}
	r := d.Roles[0]
	if r.SystemPromptPath != "overrides/fe-lead.md" {
		t.Fatalf("SystemPromptPath = %q", r.SystemPromptPath)
	}
	if got := ResolveSystemPromptPath(r); got != "overrides/fe-lead.md" {
		t.Fatalf("ResolveSystemPromptPath = %q, want override", got)
	}
	// Without override, falls back to Definition.
	r.SystemPromptPath = ""
	if got := ResolveSystemPromptPath(r); got != "roles/v1/fe-lead.md" {
		t.Fatalf("fallback ResolveSystemPromptPath = %q", got)
	}
}
