package infra

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

const wgTestSubnet = "10.42.0.0/24"

// baseConfig es un [Interface] mínimo para simular wg0.conf vacío de peers.
const wgBaseConfig = `[Interface]
Address = 10.42.0.1/24
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
ListenPort = 51820
`

func wgTempConfig(t *testing.T, content string) string {
	t.Helper()
	dir := t.TempDir()
	p := filepath.Join(dir, "wg0.conf")
	if err := os.WriteFile(p, []byte(content), 0600); err != nil {
		t.Fatalf("wgTempConfig: %v", err)
	}
	return p
}

func init() {
	// asegura que syncconf no se ejecuta en tests
	os.Setenv("WG_SKIP_SYNCCONF", "1")
}

func TestWGPeerAdd(t *testing.T) {
	t.Run("peer nuevo con AllowedIPs vacio asigna 10.42.0.2", func(t *testing.T) {
		cfg := wgTempConfig(t, wgBaseConfig)
		spec := WGPeerSpec{
			DeviceID:  "pc-aurgi",
			PublicKey: "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
		}
		res, err := WGPeerAdd(spec, cfg, wgTestSubnet)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if res.AssignedIP != "10.42.0.2" {
			t.Errorf("AssignedIP = %q, want 10.42.0.2", res.AssignedIP)
		}
		if res.Status != "added" {
			t.Errorf("Status = %q, want added", res.Status)
		}
		if res.DeviceID != "pc-aurgi" {
			t.Errorf("DeviceID = %q, want pc-aurgi", res.DeviceID)
		}
		// verificar que el bloque está en el archivo
		raw, _ := os.ReadFile(cfg)
		content := string(raw)
		if !strings.Contains(content, "# DeviceID: pc-aurgi") {
			t.Errorf("config missing DeviceID comment, got:\n%s", content)
		}
		if !strings.Contains(content, "AllowedIPs = 10.42.0.2/32") {
			t.Errorf("config missing AllowedIPs, got:\n%s", content)
		}
	})

	t.Run("agregar segundo peer asigna 10.42.0.3", func(t *testing.T) {
		cfg := wgTempConfig(t, wgBaseConfig)

		spec1 := WGPeerSpec{
			DeviceID:  "pc-aurgi",
			PublicKey: "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
		}
		if _, err := WGPeerAdd(spec1, cfg, wgTestSubnet); err != nil {
			t.Fatalf("add peer1: %v", err)
		}

		spec2 := WGPeerSpec{
			DeviceID:  "home-wsl",
			PublicKey: "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=",
		}
		res, err := WGPeerAdd(spec2, cfg, wgTestSubnet)
		if err != nil {
			t.Fatalf("add peer2: %v", err)
		}
		if res.AssignedIP != "10.42.0.3" {
			t.Errorf("AssignedIP = %q, want 10.42.0.3", res.AssignedIP)
		}
		if res.Status != "added" {
			t.Errorf("Status = %q, want added", res.Status)
		}

		raw, _ := os.ReadFile(cfg)
		content := string(raw)
		if !strings.Contains(content, "# DeviceID: home-wsl") {
			t.Errorf("config missing second DeviceID comment, got:\n%s", content)
		}
	})

	t.Run("agregar mismo PublicKey otra vez retorna already-present", func(t *testing.T) {
		cfg := wgTempConfig(t, wgBaseConfig)
		spec := WGPeerSpec{
			DeviceID:  "pc-aurgi",
			PublicKey: "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
		}
		if _, err := WGPeerAdd(spec, cfg, wgTestSubnet); err != nil {
			t.Fatalf("first add: %v", err)
		}

		res, err := WGPeerAdd(spec, cfg, wgTestSubnet)
		if err != nil {
			t.Fatalf("second add: %v", err)
		}
		if res.Status != "already-present" {
			t.Errorf("Status = %q, want already-present", res.Status)
		}
		if res.AssignedIP != "10.42.0.2" {
			t.Errorf("AssignedIP = %q, want 10.42.0.2", res.AssignedIP)
		}
	})

	t.Run("agregar DeviceID existente con clave distinta retorna reconfigured", func(t *testing.T) {
		cfg := wgTempConfig(t, wgBaseConfig)

		spec1 := WGPeerSpec{
			DeviceID:  "pc-aurgi",
			PublicKey: "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
		}
		if _, err := WGPeerAdd(spec1, cfg, wgTestSubnet); err != nil {
			t.Fatalf("first add: %v", err)
		}

		// misma DeviceID, PublicKey diferente
		spec2 := WGPeerSpec{
			DeviceID:   "pc-aurgi",
			PublicKey:  "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=",
			AllowedIPs: "10.42.0.2/32",
		}
		res, err := WGPeerAdd(spec2, cfg, wgTestSubnet)
		if err != nil {
			t.Fatalf("reconfigure: %v", err)
		}
		if res.Status != "reconfigured" {
			t.Errorf("Status = %q, want reconfigured", res.Status)
		}

		// la clave vieja no debe estar, la nueva sí
		raw, _ := os.ReadFile(cfg)
		content := string(raw)
		if strings.Contains(content, "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") {
			t.Errorf("old PublicKey still present after reconfigure:\n%s", content)
		}
		if !strings.Contains(content, "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=") {
			t.Errorf("new PublicKey not found after reconfigure:\n%s", content)
		}
		// solo debe haber un bloque [Peer]
		count := strings.Count(content, "[Peer]")
		if count != 1 {
			t.Errorf("[Peer] count = %d, want 1:\n%s", count, content)
		}
	})
}
