package infra

import (
	"encoding/base64"
	"fmt"
	"net"
	"regexp"
	"strings"

	qrcode "github.com/skip2/go-qrcode"
)

// wgEndpointRe matches "host:port" where host is a hostname or IP and port is 1–65535.
var wgEndpointRe = regexp.MustCompile(`^[a-zA-Z0-9._\-]+:\d{1,5}$`)

// WGClientConfigGen generates the wg0.conf content for a WireGuard peer (client)
// and a unicode-block QR string suitable for mobile enrollment via Element or a terminal.
//
// Pure: no I/O, fully deterministic given the inputs. Returns error on invalid inputs.
func WGClientConfigGen(in WGClientConfigInput) (WGClientConfig, error) {
	if err := validateWGClientInput(in); err != nil {
		return WGClientConfig{}, err
	}

	ka := in.PersistentKA
	if ka == 0 {
		ka = 25
	}

	var b strings.Builder

	// [Interface] section
	b.WriteString("[Interface]\n")
	fmt.Fprintf(&b, "PrivateKey = %s\n", in.DevicePrivateKey)
	fmt.Fprintf(&b, "Address = %s\n", in.DeviceAddress)
	if in.DNS != "" {
		fmt.Fprintf(&b, "DNS = %s\n", in.DNS)
	}
	b.WriteString("\n")

	// [Peer] section (hub)
	b.WriteString("[Peer]\n")
	fmt.Fprintf(&b, "PublicKey = %s\n", in.HubPublicKey)
	if in.PresharedKey != "" {
		fmt.Fprintf(&b, "PresharedKey = %s\n", in.PresharedKey)
	}
	fmt.Fprintf(&b, "Endpoint = %s\n", in.HubEndpoint)
	fmt.Fprintf(&b, "AllowedIPs = %s\n", in.HubAllowedIPs)
	fmt.Fprintf(&b, "PersistentKeepalive = %d\n", ka)

	ini := b.String()

	qr, err := qrcode.New(ini, qrcode.Medium)
	if err != nil {
		return WGClientConfig{}, fmt.Errorf("wg_client_config: qr encode: %w", err)
	}

	return WGClientConfig{
		INI:      ini,
		QR:       qr.ToString(false),
		Filename: "wg0.conf",
	}, nil
}

// validateWGClientInput checks all required fields for correctness.
func validateWGClientInput(in WGClientConfigInput) error {
	if err := validateWGBase64Key("DevicePrivateKey", in.DevicePrivateKey); err != nil {
		return err
	}
	if err := validateWGBase64Key("HubPublicKey", in.HubPublicKey); err != nil {
		return err
	}
	if in.PresharedKey != "" {
		if err := validateWGBase64Key("PresharedKey", in.PresharedKey); err != nil {
			return err
		}
	}

	// Validate DeviceAddress (CIDR)
	if _, _, err := net.ParseCIDR(in.DeviceAddress); err != nil {
		return fmt.Errorf("wg_client_config: DeviceAddress %q is not a valid CIDR: %w", in.DeviceAddress, err)
	}

	// Validate HubAllowedIPs (comma-separated CIDRs)
	for _, cidr := range strings.Split(in.HubAllowedIPs, ",") {
		cidr = strings.TrimSpace(cidr)
		if cidr == "" {
			continue
		}
		if _, _, err := net.ParseCIDR(cidr); err != nil {
			return fmt.Errorf("wg_client_config: HubAllowedIPs entry %q is not a valid CIDR: %w", cidr, err)
		}
	}

	// Validate HubEndpoint
	if !wgEndpointRe.MatchString(in.HubEndpoint) {
		return fmt.Errorf("wg_client_config: HubEndpoint %q must be host:port", in.HubEndpoint)
	}
	parts := strings.SplitN(in.HubEndpoint, ":", 2)
	port := 0
	if _, err := fmt.Sscanf(parts[1], "%d", &port); err != nil || port < 1 || port > 65535 {
		return fmt.Errorf("wg_client_config: HubEndpoint port %q out of range 1-65535", parts[1])
	}

	if in.DevicePrivateKey == "" {
		return fmt.Errorf("wg_client_config: DevicePrivateKey is required")
	}
	if in.HubPublicKey == "" {
		return fmt.Errorf("wg_client_config: HubPublicKey is required")
	}
	if in.HubEndpoint == "" {
		return fmt.Errorf("wg_client_config: HubEndpoint is required")
	}
	if in.HubAllowedIPs == "" {
		return fmt.Errorf("wg_client_config: HubAllowedIPs is required")
	}

	return nil
}

// validateWGBase64Key checks that a WireGuard key is a valid 32-byte base64-encoded string (44 chars).
func validateWGBase64Key(field, key string) error {
	if len(key) != 44 {
		return fmt.Errorf("wg_client_config: %s must be 44 base64 chars, got %d", field, len(key))
	}
	decoded, err := base64.StdEncoding.DecodeString(key)
	if err != nil {
		return fmt.Errorf("wg_client_config: %s is not valid base64: %w", field, err)
	}
	if len(decoded) != 32 {
		return fmt.Errorf("wg_client_config: %s must decode to 32 bytes, got %d", field, len(decoded))
	}
	return nil
}
