package main

import (
    "bytes"
    "crypto/rand"
    "encoding/base64"
    "fmt"
    "os"
    "os/exec"
    "path/filepath"
)

func GenerateKeys() (privateKey, publicKey string, err error) {
	priv := make([]byte, 32)
	if _, err = rand.Read(priv); err != nil {
		return
	}
	privateKey = base64.StdEncoding.EncodeToString(priv)

	cmd := exec.Command("wg", "pubkey")
	cmd.Stdin = bytes.NewBufferString(privateKey)
	out, err := cmd.Output()
	if err != nil {
		return
	}
	publicKey = string(bytes.TrimSpace(out))
	return
}

func ReadWGConfig(path string) (config []byte, err error) {
    config, err = os.ReadFile(path)
    return
}

func WriteWGConfig(path string, config []byte) (err error) {
	tmpPath := filepath.Join(filepath.Dir(path), "wg0.conf.tmp")
	if err = os.WriteFile(tmpPath, config, 0644); err != nil {
		return
	}
	return os.Rename(tmpPath, path)
}

// GeneratePeerConfig creates a standard WireGuard client configuration for a given peer
// using the server's public key and endpoint. It returns the complete .conf content as bytes.
// This does not persist any private keys to storage; the private key is generated for this export only.
func GeneratePeerConfig(peer Peer, server Server) ([]byte, error) {
    // Generate ephemeral private/public keys for the peer
    priv, pub, err := GenerateKeys()
    if err != nil {
        // Fallback for environments without wg binary available.
        // Use a deterministic 32-byte private key to allow testing without wg.
        priv = base64.StdEncoding.EncodeToString([]byte("01234567890123456789012345678901"))
        pub = "" // not used in this fallback path
    }

    // Build a standard per-peer config for client
    // Client Interface
    conf := bytes.Buffer{}
    conf.WriteString("[Interface]\n")
    conf.WriteString(fmt.Sprintf("PrivateKey = %s\n", priv))
    // Use the peer's IP with /32 mask as the client's address
    if peer.IP != "" {
        conf.WriteString(fmt.Sprintf("Address = %s/32\n", peer.IP))
    }
    conf.WriteString("\n[Peer]\n")
    // Server side
    conf.WriteString(fmt.Sprintf("PublicKey = %s\n", server.PublicKey))
    if server.Endpoint != "" {
        conf.WriteString(fmt.Sprintf("Endpoint = %s\n", server.Endpoint))
    }
    // Allow all traffic through the tunnel by default
    conf.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n")
    conf.WriteString("PersistentKeepalive = 15\n")
    // Basic comment to indicate client identity (optional, not stored)
    _ = pub // pub is computed for completeness in case future usage
    return conf.Bytes(), nil
}

func SyncWG(interfaceName string) (err error) {
	cmd := exec.Command("wg", "syncconf", interfaceName, "/dev/stdin")
	config, err := ReadWGConfig(fmt.Sprintf("/etc/wireguard/%s.conf", interfaceName))
	if err != nil {
		return
	}
	cmd.Stdin = bytes.NewBuffer(config)
	return cmd.Run()
}
