// Package tor - Embedded Tor implementation for military/war scenarios
package tor

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

// EmbeddedTor manages an embedded Tor instance
type EmbeddedTor struct {
	torBinary    string          // Path to Tor executable
	torDataDir   string          // Tor data directory
	torProcess   *exec.Cmd       // Tor subprocess
	controlPort  int             // Tor control port
	socksPort    int             // Tor SOCKS proxy port
	hiddenSvcDir string          // Hidden service directory
	onionAddress string          // Our .onion address
	running      bool
	mu           sync.RWMutex
	useBridges   bool            // Use bridges for censorship circumvention
	bridges      []string        // Bridge addresses
}

// EmbeddedTorConfig holds configuration for embedded Tor
type EmbeddedTorConfig struct {
	DataDir      string   // Where to store Tor data
	UseBridges   bool     // Enable bridge mode (anti-censorship)
	Bridges      []string // Bridge addresses (obfs4, meek, etc.)
	ControlPort  int      // Control port (0 = auto)
	SocksPort    int      // SOCKS proxy port (0 = auto)
	AutoStart    bool     // Auto-start Tor on creation
}

// NewEmbeddedTor creates a new embedded Tor instance
func NewEmbeddedTor(config *EmbeddedTorConfig) (*EmbeddedTor, error) {
	if config == nil {
		config = &EmbeddedTorConfig{
			AutoStart:   true,
			ControlPort: 0, // Auto-select
			SocksPort:   0, // Auto-select
		}
	}

	// Create data directory
	dataDir := config.DataDir
	if dataDir == "" {
		// Use temp directory
		tmpDir := os.TempDir()
		dataDir = filepath.Join(tmpDir, "khimera-tor-"+randomString(8))
	}

	if err := os.MkdirAll(dataDir, 0700); err != nil {
		return nil, fmt.Errorf("failed to create Tor data dir: %w", err)
	}

	et := &EmbeddedTor{
		torDataDir:   dataDir,
		hiddenSvcDir: filepath.Join(dataDir, "hidden_service"),
		controlPort:  config.ControlPort,
		socksPort:    config.SocksPort,
		useBridges:   config.UseBridges,
		bridges:      config.Bridges,
	}

	// Find Tor binary
	torBin, err := et.findTorBinary()
	if err != nil {
		return nil, fmt.Errorf("Tor binary not found: %w", err)
	}
	et.torBinary = torBin

	// Auto-assign ports if not specified
	if et.controlPort == 0 {
		et.controlPort = findFreePort()
	}
	if et.socksPort == 0 {
		et.socksPort = findFreePort()
	}

	// Auto-start if requested
	if config.AutoStart {
		if err := et.Start(); err != nil {
			return nil, fmt.Errorf("failed to auto-start Tor: %w", err)
		}
	}

	return et, nil
}

// findTorBinary locates the Tor binary
func (et *EmbeddedTor) findTorBinary() (string, error) {
	// Try common locations
	possiblePaths := []string{
		"tor",                                    // PATH
		"/usr/bin/tor",                           // Linux
		"/usr/local/bin/tor",                     // macOS/BSD
		"C:\\Program Files\\Tor Browser\\tor.exe", // Windows
		filepath.Join(et.torDataDir, "tor"),      // Bundled with app
	}

	// Add OS-specific paths
	if runtime.GOOS == "windows" {
		possiblePaths = append(possiblePaths, "tor.exe")
	}

	for _, path := range possiblePaths {
		if _, err := os.Stat(path); err == nil {
			return path, nil
		}
		// Also try in PATH
		if fullPath, err := exec.LookPath(path); err == nil {
			return fullPath, nil
		}
	}

	return "", fmt.Errorf("Tor binary not found in common locations")
}

// Start starts the embedded Tor process
func (et *EmbeddedTor) Start() error {
	et.mu.Lock()
	defer et.mu.Unlock()

	if et.running {
		return fmt.Errorf("Tor already running")
	}

	// Create torrc configuration
	torrcPath := filepath.Join(et.torDataDir, "torrc")
	if err := et.writeTorrc(torrcPath); err != nil {
		return fmt.Errorf("failed to write torrc: %w", err)
	}

	// Launch Tor process
	et.torProcess = exec.Command(et.torBinary, "-f", torrcPath)
	et.torProcess.Stdout = os.Stdout // For debugging
	et.torProcess.Stderr = os.Stderr

	if err := et.torProcess.Start(); err != nil {
		return fmt.Errorf("failed to start Tor: %w", err)
	}

	et.running = true

	// Wait for Tor to bootstrap
	if err := et.waitForBootstrap(60 * time.Second); err != nil {
		et.Stop()
		return fmt.Errorf("Tor bootstrap failed: %w", err)
	}

	// Read hidden service address
	if err := et.readOnionAddress(); err != nil {
		return fmt.Errorf("failed to read onion address: %w", err)
	}

	return nil
}

// writeTorrc writes Tor configuration file
func (et *EmbeddedTor) writeTorrc(path string) error {
	config := fmt.Sprintf(`# Khimera Embedded Tor Configuration

# Data directory
DataDirectory %s

# SOCKS proxy
SOCKSPort %d

# Control port
ControlPort %d

# Hidden service
HiddenServiceDir %s
HiddenServicePort 8083 127.0.0.1:8083

# Security settings
SafeLogging 1
AvoidDiskWrites 1

`, et.torDataDir, et.socksPort, et.controlPort, et.hiddenSvcDir)

	// Add bridges if anti-censorship mode
	if et.useBridges && len(et.bridges) > 0 {
		config += "\n# Bridge mode (anti-censorship)\n"
		config += "UseBridges 1\n"
		config += "ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy\n"
		for _, bridge := range et.bridges {
			config += fmt.Sprintf("Bridge %s\n", bridge)
		}
	}

	return os.WriteFile(path, []byte(config), 0600)
}

// waitForBootstrap waits for Tor to finish bootstrapping
func (et *EmbeddedTor) waitForBootstrap(timeout time.Duration) error {
	start := time.Now()

	for {
		if time.Since(start) > timeout {
			return fmt.Errorf("bootstrap timeout")
		}

		// Try to connect to SOCKS port
		conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", et.socksPort), 1*time.Second)
		if err == nil {
			conn.Close()
			// Successfully connected - Tor is ready
			return nil
		}

		time.Sleep(1 * time.Second)
	}
}

// readOnionAddress reads the hidden service .onion address
func (et *EmbeddedTor) readOnionAddress() error {
	hostnamePath := filepath.Join(et.hiddenSvcDir, "hostname")

	// Wait for file to be created (may take a few seconds)
	for i := 0; i < 30; i++ {
		data, err := os.ReadFile(hostnamePath)
		if err == nil {
			et.onionAddress = strings.TrimSpace(string(data))
			return nil
		}
		time.Sleep(1 * time.Second)
	}

	return fmt.Errorf("failed to read onion address from %s", hostnamePath)
}

// Stop stops the embedded Tor process
func (et *EmbeddedTor) Stop() error {
	et.mu.Lock()
	defer et.mu.Unlock()

	if !et.running {
		return nil
	}

	if et.torProcess != nil {
		// Graceful shutdown
		if err := et.torProcess.Process.Signal(os.Interrupt); err != nil {
			// Force kill if graceful fails
			et.torProcess.Process.Kill()
		}

		// Wait for process to exit
		et.torProcess.Wait()
	}

	et.running = false
	return nil
}

// GetSOCKSProxy returns the SOCKS proxy address
func (et *EmbeddedTor) GetSOCKSProxy() string {
	et.mu.RLock()
	defer et.mu.RUnlock()
	return fmt.Sprintf("127.0.0.1:%d", et.socksPort)
}

// GetOnionAddress returns our hidden service .onion address
func (et *EmbeddedTor) GetOnionAddress() string {
	et.mu.RLock()
	defer et.mu.RUnlock()
	return et.onionAddress
}

// IsRunning returns whether Tor is currently running
func (et *EmbeddedTor) IsRunning() bool {
	et.mu.RLock()
	defer et.mu.RUnlock()
	return et.running
}

// Cleanup removes Tor data directory
func (et *EmbeddedTor) Cleanup() error {
	et.Stop()

	if et.torDataDir != "" {
		return os.RemoveAll(et.torDataDir)
	}
	return nil
}

// SetBridges configures Tor bridges for censorship circumvention
func (et *EmbeddedTor) SetBridges(bridges []string) error {
	et.mu.Lock()
	defer et.mu.Unlock()

	et.useBridges = true
	et.bridges = bridges

	// Restart Tor if running to apply new config
	if et.running {
		et.mu.Unlock() // Unlock before calling Stop (which locks)
		et.Stop()
		et.mu.Lock()
		return et.Start()
	}

	return nil
}

// GetDefaultBridges returns a list of default obfs4 bridges
func GetDefaultBridges() []string {
	return []string{
		"obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1",
		"obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0",
		"obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0",
	}
}

// findFreePort finds an available TCP port
func findFreePort() int {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return 9050 // Fallback to default
	}
	defer listener.Close()

	addr := listener.Addr().(*net.TCPAddr)
	return addr.Port
}

// randomString generates a random hex string
func randomString(n int) string {
	bytes := make([]byte, n)
	io.ReadFull(rand.Reader, bytes)
	return hex.EncodeToString(bytes)
}

// String returns a string representation
func (et *EmbeddedTor) String() string {
	et.mu.RLock()
	defer et.mu.RUnlock()

	status := "stopped"
	if et.running {
		status = "running"
	}

	return fmt.Sprintf("EmbeddedTor(status=%s, socks=%d, onion=%s)",
		status, et.socksPort, et.onionAddress)
}
