//go:build linux

package usbip

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

const vhciBasePath = "/sys/devices/platform/vhci_hcd.0"

// VHCIPort represents a virtual USB port on the VHCI controller
type VHCIPort struct {
	Hub        string // "hs" or "ss"
	Port       int
	Status     int
	Speed      int
	DevID      uint32
	SocketFD   int
	LocalBusID string
}

// VHCI status constants
const (
	VDevStNull        = 0x04
	VDevStNotAssigned = 0x05
	VDevStUsed        = 0x06
	VDevStError       = 0x07
)

// ReadVHCIStatus reads the current VHCI port status
func ReadVHCIStatus() ([]VHCIPort, error) {
	// Try status file directly, then status.0, status.1, etc.
	var allPorts []VHCIPort

	paths := []string{
		filepath.Join(vhciBasePath, "status"),
	}

	// Check for multi-controller status files
	for i := 0; i < 16; i++ {
		p := filepath.Join(vhciBasePath, fmt.Sprintf("status.%d", i))
		if _, err := os.Stat(p); err == nil {
			paths = append(paths, p)
		} else {
			break
		}
	}

	for _, path := range paths {
		ports, err := parseStatusFile(path)
		if err != nil {
			continue
		}
		allPorts = append(allPorts, ports...)
	}

	if len(allPorts) == 0 {
		return nil, fmt.Errorf("vhci-hcd module not loaded or no ports found (check: modprobe vhci-hcd)")
	}

	return allPorts, nil
}

func parseStatusFile(path string) ([]VHCIPort, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}

	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	var ports []VHCIPort

	for _, line := range lines {
		line = strings.TrimSpace(line)
		// Skip header lines
		if strings.HasPrefix(line, "hub") || strings.HasPrefix(line, "prt") || line == "" {
			continue
		}

		fields := strings.Fields(line)
		if len(fields) < 7 {
			continue
		}

		port := VHCIPort{Hub: fields[0]}

		if v, err := strconv.Atoi(fields[1]); err == nil {
			port.Port = v
		}
		if v, err := strconv.Atoi(fields[2]); err == nil {
			port.Status = v
		}
		if v, err := strconv.Atoi(fields[3]); err == nil {
			port.Speed = v
		}
		if v, err := strconv.ParseUint(fields[4], 16, 32); err == nil {
			port.DevID = uint32(v)
		}
		if v, err := strconv.Atoi(fields[5]); err == nil {
			port.SocketFD = v
		}
		port.LocalBusID = fields[6]

		ports = append(ports, port)
	}

	return ports, nil
}

// FindFreePort finds an available VHCI port for the given speed
func FindFreePort(speed uint32) (int, error) {
	ports, err := ReadVHCIStatus()
	if err != nil {
		return -1, err
	}

	// Determine desired hub type based on speed
	wantHub := "hs" // high-speed and below
	if speed >= SpeedSuper {
		wantHub = "ss" // super-speed
	}

	for _, port := range ports {
		if port.Status == VDevStNull && port.Hub == wantHub {
			return port.Port, nil
		}
	}

	return -1, fmt.Errorf("no free VHCI port available for hub type %s", wantHub)
}

// AttachDevice writes to the VHCI attach file to create a virtual USB device.
// sockfd must be a valid TCP socket file descriptor connected to the USB/IP server.
func AttachDevice(port int, sockfd int, devID uint32, speed uint32) error {
	attachPath := filepath.Join(vhciBasePath, "attach")

	// Format: "<port> <sockfd> <devid> <speed>"
	data := fmt.Sprintf("%d %d %d %d", port, sockfd, devID, speed)

	if err := os.WriteFile(attachPath, []byte(data), 0); err != nil {
		return fmt.Errorf("writing to VHCI attach: %w", err)
	}

	return nil
}

// DetachDevice writes to the VHCI detach file to remove a virtual USB device
func DetachDevice(port int) error {
	detachPath := filepath.Join(vhciBasePath, "detach")

	data := fmt.Sprintf("%d", port)

	if err := os.WriteFile(detachPath, []byte(data), 0); err != nil {
		return fmt.Errorf("writing to VHCI detach: %w", err)
	}

	return nil
}

// IsVHCIAvailable checks if the vhci-hcd kernel module is loaded
func IsVHCIAvailable() bool {
	_, err := os.Stat(vhciBasePath)
	return err == nil
}

// VHCIUnavailableError returns an error describing why VHCI is not available,
// or nil if VHCI is ready to use.
func VHCIUnavailableError() error {
	if IsVHCIAvailable() {
		return nil
	}
	return fmt.Errorf("vhci-hcd Kernel-Modul nicht geladen (ausfuehren: sudo modprobe vhci-hcd)")
}
