package config

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

func writeTestFile(t *testing.T, dir, name, content string) string {
	t.Helper()
	p := filepath.Join(dir, name)
	if err := os.WriteFile(p, []byte(content), 0644); err != nil {
		t.Fatalf("write %s: %v", p, err)
	}
	return p
}

const exampleMarkdown = `---
listen_addr: "127.0.0.1:9999"
db_path: "/tmp/orca/test.db"
ca_path: "/tmp/orca/ca.crt"
server_cert_path: "/tmp/orca/server.crt"
server_key_path: "/tmp/orca/server.key"
node_capacity:
  cpu: 4
  memory_mb: 8192
---

# Orca config

This is prose body and is ignored by the loader.
`

func TestLoadMarkdown_Full(t *testing.T) {
	p := writeTestFile(t, t.TempDir(), "config.md", exampleMarkdown)
	cfg, err := LoadMarkdown(p)
	if err != nil {
		t.Fatalf("LoadMarkdown: %v", err)
	}
	if cfg.DBPath != "/tmp/orca/test.db" {
		t.Errorf("DBPath=%q", cfg.DBPath)
	}
	if cfg.ListenAddr != "127.0.0.1:9999" {
		t.Errorf("ListenAddr=%q", cfg.ListenAddr)
	}
	if cfg.CAPath != "/tmp/orca/ca.crt" {
		t.Errorf("CAPath=%q", cfg.CAPath)
	}
	if cfg.ServerCertPath != "/tmp/orca/server.crt" {
		t.Errorf("ServerCertPath=%q", cfg.ServerCertPath)
	}
	if cfg.ServerKeyPath != "/tmp/orca/server.key" {
		t.Errorf("ServerKeyPath=%q", cfg.ServerKeyPath)
	}
	if cfg.NodeCapacity == nil {
		t.Fatal("NodeCapacity nil")
	}
	if cfg.NodeCapacity.CPU != 4 {
		t.Errorf("CPU=%d", cfg.NodeCapacity.CPU)
	}
	if cfg.NodeCapacity.MemoryMB != 8192 {
		t.Errorf("MemoryMB=%d", cfg.NodeCapacity.MemoryMB)
	}
}

func TestLoadMarkdown_NoFrontmatter(t *testing.T) {
	// No delimiters: whole file treated as a bare YAML block.
	body := "listen_addr: 0.0.0.0:1234\ndb_path: /x/y.db\n"
	p := writeTestFile(t, t.TempDir(), "config.md", body)
	cfg, err := LoadMarkdown(p)
	if err != nil {
		t.Fatalf("LoadMarkdown: %v", err)
	}
	if cfg.ListenAddr != "0.0.0.0:1234" {
		t.Errorf("ListenAddr=%q", cfg.ListenAddr)
	}
	if cfg.DBPath != "/x/y.db" {
		t.Errorf("DBPath=%q", cfg.DBPath)
	}
}

func TestLoadMarkdown_OnlyBody(t *testing.T) {
	body := `---
---

# Just prose, no keys
`
	p := writeTestFile(t, t.TempDir(), "config.md", body)
	cfg, err := LoadMarkdown(p)
	if err != nil {
		t.Fatalf("LoadMarkdown: %v", err)
	}
	if cfg.DBPath != "" || cfg.ListenAddr != "" || cfg.NodeCapacity != nil {
		t.Errorf("expected zero config, got %+v", cfg)
	}
}

func TestLoadMarkdown_CommentsAndBlanks(t *testing.T) {
	body := `---
# a comment
listen_addr: "127.0.0.1:9999"

db_path: "/tmp/orca/test.db"  # inline comment

node_capacity:
  cpu: 4   # cores
  memory_mb: 8192
---
`
	p := writeTestFile(t, t.TempDir(), "config.md", body)
	cfg, err := LoadMarkdown(p)
	if err != nil {
		t.Fatalf("LoadMarkdown: %v", err)
	}
	if cfg.ListenAddr != "127.0.0.1:9999" {
		t.Errorf("ListenAddr=%q", cfg.ListenAddr)
	}
	if cfg.DBPath != "/tmp/orca/test.db" {
		t.Errorf("DBPath=%q", cfg.DBPath)
	}
	if cfg.NodeCapacity == nil || cfg.NodeCapacity.CPU != 4 || cfg.NodeCapacity.MemoryMB != 8192 {
		t.Errorf("NodeCapacity=%+v", cfg.NodeCapacity)
	}
}

func TestLoadMarkdownYAML_Bare(t *testing.T) {
	body := "listen_addr: 0.0.0.0:5555\ndb_path: /bare.db\nnode_capacity:\n  cpu: 2\n  memory_mb: 4096\n"
	p := writeTestFile(t, t.TempDir(), "config.yaml", body)
	cfg, err := LoadMarkdownYAML(p)
	if err != nil {
		t.Fatalf("LoadMarkdownYAML: %v", err)
	}
	if cfg.ListenAddr != "0.0.0.0:5555" {
		t.Errorf("ListenAddr=%q", cfg.ListenAddr)
	}
	if cfg.DBPath != "/bare.db" {
		t.Errorf("DBPath=%q", cfg.DBPath)
	}
	if cfg.NodeCapacity == nil || cfg.NodeCapacity.CPU != 2 || cfg.NodeCapacity.MemoryMB != 4096 {
		t.Errorf("NodeCapacity=%+v", cfg.NodeCapacity)
	}
}

func TestLoadMarkdown_ReadError(t *testing.T) {
	missing := filepath.Join(t.TempDir(), "nope.md")
	if _, err := LoadMarkdown(missing); err == nil {
		t.Fatal("expected error for missing file")
	}
}

func TestExtractFrontmatter(t *testing.T) {
	cases := []struct {
		name    string
		input   string
		block   string
		present bool
	}{
		{"standard", "---\nkey: val\n---\nbody", "key: val", true},
		{"leading-blanks", "\n\n---\nkey: val\n---\n", "key: val", true},
		{"no-delimiters", "key: val\n", "key: val", false},
		{"only-open", "---\nkey: val\n", "key: val", false},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			block, ok := extractFrontmatter(tc.input)
			if ok != tc.present {
				t.Errorf("present=%v want %v", ok, tc.present)
			}
			if tc.present && block != tc.block {
				t.Errorf("block=%q want %q", block, tc.block)
			}
		})
	}
}

func TestUnquote(t *testing.T) {
	if got, want := unquote(`"hello"`), "hello"; got != want {
		t.Errorf("unquote double = %q want %q", got, want)
	}
	if got, want := unquote(`'hello'`), "hello"; got != want {
		t.Errorf("unquote single = %q want %q", got, want)
	}
	if got, want := unquote("bare"), "bare"; got != want {
		t.Errorf("unquote bare = %q want %q", got, want)
	}
}
