package infra

import (
	"strings"
	"testing"
)

func TestSSHConfigParse_MultipleHosts(t *testing.T) {
	content := `
Host prod
    HostName 10.0.0.1
    User admin
    Port 22
    IdentityFile ~/.ssh/id_prod

Host staging
    HostName 10.0.0.2
    User deploy
    Port 2222
`
	entries := SSHConfigParse(content)
	if len(entries) != 2 {
		t.Fatalf("expected 2 entries, got %d", len(entries))
	}
	if entries[0].Alias != "prod" {
		t.Errorf("expected alias prod, got %s", entries[0].Alias)
	}
	if entries[0].HostName != "10.0.0.1" {
		t.Errorf("expected hostname 10.0.0.1, got %s", entries[0].HostName)
	}
	if entries[0].User != "admin" {
		t.Errorf("expected user admin, got %s", entries[0].User)
	}
	if entries[0].Port != 22 {
		t.Errorf("expected port 22, got %d", entries[0].Port)
	}
	if entries[0].IdentityFile != "~/.ssh/id_prod" {
		t.Errorf("expected identity file ~/.ssh/id_prod, got %s", entries[0].IdentityFile)
	}
	if entries[1].Alias != "staging" {
		t.Errorf("expected alias staging, got %s", entries[1].Alias)
	}
	if entries[1].Port != 2222 {
		t.Errorf("expected port 2222, got %d", entries[1].Port)
	}
}

func TestSSHConfigParse_IgnoresCommentsAndBlanks(t *testing.T) {
	content := `
# This is a comment
Host myserver
    HostName 192.168.1.1
    # inline comment
    User root
`
	entries := SSHConfigParse(content)
	if len(entries) != 1 {
		t.Fatalf("expected 1 entry, got %d", len(entries))
	}
	if entries[0].User != "root" {
		t.Errorf("expected user root, got %s", entries[0].User)
	}
}

func TestSSHConfigParse_IgnoresWildcards(t *testing.T) {
	content := `
Host *
    ServerAliveInterval 60

Host prod
    HostName 10.0.0.1

Host 192.168.*
    User local
`
	entries := SSHConfigParse(content)
	if len(entries) != 1 {
		t.Fatalf("expected 1 entry (wildcards ignored), got %d", len(entries))
	}
	if entries[0].Alias != "prod" {
		t.Errorf("expected alias prod, got %s", entries[0].Alias)
	}
}

func TestSSHConfigParse_ExtraOptions(t *testing.T) {
	content := `Host jump
    HostName bastion.example.com
    User ops
    ForwardAgent yes
    ProxyJump none
`
	entries := SSHConfigParse(content)
	if len(entries) != 1 {
		t.Fatalf("expected 1 entry, got %d", len(entries))
	}
	if entries[0].Options["ForwardAgent"] != "yes" {
		t.Errorf("expected ForwardAgent=yes, got %s", entries[0].Options["ForwardAgent"])
	}
	if entries[0].Options["ProxyJump"] != "none" {
		t.Errorf("expected ProxyJump=none, got %s", entries[0].Options["ProxyJump"])
	}
}

func TestSSHConfigRender_FullEntry(t *testing.T) {
	entries := []SSHConfigEntry{{
		Alias:        "prod",
		HostName:     "10.0.0.1",
		User:         "admin",
		Port:         2222,
		IdentityFile: "~/.ssh/id_prod",
	}}
	result := SSHConfigRender(entries)
	if !strings.Contains(result, "Host prod") {
		t.Error("missing Host directive")
	}
	if !strings.Contains(result, "HostName 10.0.0.1") {
		t.Error("missing HostName")
	}
	if !strings.Contains(result, "User admin") {
		t.Error("missing User")
	}
	if !strings.Contains(result, "Port 2222") {
		t.Error("missing Port")
	}
	if !strings.Contains(result, "IdentityFile ~/.ssh/id_prod") {
		t.Error("missing IdentityFile")
	}
}

func TestSSHConfigRender_MinimalEntry(t *testing.T) {
	entries := []SSHConfigEntry{{Alias: "local"}}
	result := SSHConfigRender(entries)
	if result != "Host local\n" {
		t.Errorf("expected minimal render, got %q", result)
	}
}

func TestSSHConfigRender_MultipleEntries(t *testing.T) {
	entries := []SSHConfigEntry{
		{Alias: "a", HostName: "1.1.1.1"},
		{Alias: "b", HostName: "2.2.2.2"},
	}
	result := SSHConfigRender(entries)
	parts := strings.Split(result, "\n\n")
	if len(parts) < 2 {
		t.Error("expected blocks separated by blank line")
	}
}

func TestSSHConfigRender_OptionsSorted(t *testing.T) {
	entries := []SSHConfigEntry{{
		Alias:    "test",
		HostName: "1.1.1.1",
		Options:  map[string]string{"ProxyJump": "bastion", "ForwardAgent": "yes"},
	}}
	result := SSHConfigRender(entries)
	fwIdx := strings.Index(result, "ForwardAgent")
	pjIdx := strings.Index(result, "ProxyJump")
	if fwIdx < 0 || pjIdx < 0 {
		t.Fatal("missing options in render")
	}
	if fwIdx > pjIdx {
		t.Error("options should be sorted alphabetically")
	}
}

func TestSSHConfigFind_Exists(t *testing.T) {
	entries := []SSHConfigEntry{
		{Alias: "prod", HostName: "10.0.0.1"},
		{Alias: "staging", HostName: "10.0.0.2"},
	}
	entry, ok := SSHConfigFind(entries, "staging")
	if !ok {
		t.Fatal("expected to find staging")
	}
	if entry.HostName != "10.0.0.2" {
		t.Errorf("expected hostname 10.0.0.2, got %s", entry.HostName)
	}
}

func TestSSHConfigFind_NotExists(t *testing.T) {
	entries := []SSHConfigEntry{{Alias: "prod"}}
	_, ok := SSHConfigFind(entries, "nope")
	if ok {
		t.Error("expected not found")
	}
}

func TestSSHConfigAddEntry_ToEmpty(t *testing.T) {
	entry := SSHConfigEntry{Alias: "new", HostName: "1.1.1.1"}
	result, err := SSHConfigAddEntry(nil, entry)
	if err != nil {
		t.Fatal(err)
	}
	if len(result) != 1 || result[0].Alias != "new" {
		t.Errorf("unexpected result: %+v", result)
	}
}

func TestSSHConfigAddEntry_ToExisting(t *testing.T) {
	existing := []SSHConfigEntry{{Alias: "old"}}
	entry := SSHConfigEntry{Alias: "new"}
	result, err := SSHConfigAddEntry(existing, entry)
	if err != nil {
		t.Fatal(err)
	}
	if len(result) != 2 {
		t.Fatalf("expected 2 entries, got %d", len(result))
	}
	// Original no mutado
	if len(existing) != 1 {
		t.Error("original slice was mutated")
	}
}

func TestSSHConfigAddEntry_DuplicateAlias(t *testing.T) {
	existing := []SSHConfigEntry{{Alias: "prod"}}
	entry := SSHConfigEntry{Alias: "prod"}
	_, err := SSHConfigAddEntry(existing, entry)
	if err == nil {
		t.Error("expected error for duplicate alias")
	}
}

func TestSSHConfigRemoveEntry_Exists(t *testing.T) {
	entries := []SSHConfigEntry{
		{Alias: "a"},
		{Alias: "b"},
		{Alias: "c"},
	}
	result, err := SSHConfigRemoveEntry(entries, "b")
	if err != nil {
		t.Fatal(err)
	}
	if len(result) != 2 {
		t.Fatalf("expected 2 entries, got %d", len(result))
	}
	if result[0].Alias != "a" || result[1].Alias != "c" {
		t.Errorf("unexpected order: %+v", result)
	}
	// Original no mutado
	if len(entries) != 3 {
		t.Error("original slice was mutated")
	}
}

func TestSSHConfigRemoveEntry_NotExists(t *testing.T) {
	entries := []SSHConfigEntry{{Alias: "prod"}}
	_, err := SSHConfigRemoveEntry(entries, "nope")
	if err == nil {
		t.Error("expected error for missing alias")
	}
}

func TestSSHConfigParseRender_Roundtrip(t *testing.T) {
	original := []SSHConfigEntry{
		{Alias: "prod", HostName: "10.0.0.1", User: "admin", Port: 22, IdentityFile: "~/.ssh/id_prod"},
		{Alias: "staging", HostName: "10.0.0.2", User: "deploy", Port: 2222},
	}
	text := SSHConfigRender(original)
	parsed := SSHConfigParse(text)
	if len(parsed) != len(original) {
		t.Fatalf("roundtrip: expected %d entries, got %d", len(original), len(parsed))
	}
	for i := range original {
		if parsed[i].Alias != original[i].Alias {
			t.Errorf("roundtrip[%d]: alias mismatch %s != %s", i, parsed[i].Alias, original[i].Alias)
		}
		if parsed[i].HostName != original[i].HostName {
			t.Errorf("roundtrip[%d]: hostname mismatch", i)
		}
		if parsed[i].User != original[i].User {
			t.Errorf("roundtrip[%d]: user mismatch", i)
		}
		if parsed[i].Port != original[i].Port {
			t.Errorf("roundtrip[%d]: port mismatch", i)
		}
	}
}
