package handlers

import (
	"net/http"
	"regexp"
	"strings"

	"github.com/gin-gonic/gin"
)

type analyseHardwareRequest struct {
	Type  string `json:"type"`
	Texte string `json:"texte"`
}

type lspciDevice struct {
	Slot        string   `json:"slot"`
	Class       string   `json:"class"`
	Description string   `json:"description"`
	Subsystem   string   `json:"subsystem,omitempty"`
	Driver      string   `json:"driver,omitempty"`
	Modules     []string `json:"modules,omitempty"`
	Raw         []string `json:"raw,omitempty"`
}

type lsusbDevice struct {
	Bus         string `json:"bus"`
	Device      string `json:"device"`
	VendorID    string `json:"vendor_id"`
	ProductID   string `json:"product_id"`
	Description string `json:"description"`
	Raw         string `json:"raw,omitempty"`
}

type analyseHardwareResponse struct {
	Type    string        `json:"type"`
	Count   int           `json:"count"`
	Devices []interface{} `json:"devices"`
}

// @Summary Analyser une sortie hardware (lspci/lsusb)
// @Tags Analyse
// @Accept json
// @Produce json
// @Param body body analyseHardwareRequest true "Texte a analyser"
// @Success 200 {object} analyseHardwareResponse
// @Failure 400 {object} map[string]string
// @Router /analyse-hardware [post]
func (h *Handler) AnalyseHardware(c *gin.Context) {
	var req analyseHardwareRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "donnees invalides"})
		return
	}
	req.Type = strings.ToLower(strings.TrimSpace(req.Type))
	req.Texte = strings.TrimSpace(req.Texte)
	if req.Texte == "" {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "texte vide"})
		return
	}

	if req.Type == "" {
		req.Type = detectHardwareType(req.Texte)
	}

	switch req.Type {
	case "lspci":
		devices := parseLspci(req.Texte)
		c.JSON(http.StatusOK, analyseHardwareResponse{
			Type:    "lspci",
			Count:   len(devices),
			Devices: toInterfaceSlice(devices),
		})
	case "lsusb":
		devices := parseLsusb(req.Texte)
		c.JSON(http.StatusOK, analyseHardwareResponse{
			Type:    "lsusb",
			Count:   len(devices),
			Devices: toInterfaceSlice(devices),
		})
	default:
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "type non supporte"})
	}
}

func detectHardwareType(text string) string {
	if strings.Contains(text, "Bus ") && strings.Contains(text, "ID ") {
		return "lsusb"
	}
	return "lspci"
}

func parseLspci(text string) []lspciDevice {
	lines := strings.Split(text, "\n")
	var devices []lspciDevice
	re := regexp.MustCompile(`^(?:[0-9a-fA-F]{4}:)?([0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])\s+([^:]+):\s+(.+)$`)

	var current *lspciDevice
	for _, raw := range lines {
		line := strings.TrimRight(raw, "\r")
		if line == "" {
			continue
		}
		if matches := re.FindStringSubmatch(line); matches != nil {
			if current != nil {
				devices = append(devices, *current)
			}
			current = &lspciDevice{
				Slot:        matches[1],
				Class:       strings.TrimSpace(matches[2]),
				Description: strings.TrimSpace(matches[3]),
				Raw:         []string{line},
			}
			continue
		}
		if current == nil {
			continue
		}
		trimmed := strings.TrimSpace(line)
		current.Raw = append(current.Raw, line)
		if strings.HasPrefix(trimmed, "Subsystem:") {
			current.Subsystem = strings.TrimSpace(strings.TrimPrefix(trimmed, "Subsystem:"))
		} else if strings.HasPrefix(trimmed, "Kernel driver in use:") {
			current.Driver = strings.TrimSpace(strings.TrimPrefix(trimmed, "Kernel driver in use:"))
		} else if strings.HasPrefix(trimmed, "Kernel modules:") {
			value := strings.TrimSpace(strings.TrimPrefix(trimmed, "Kernel modules:"))
			if value != "" {
				current.Modules = splitCSV(value)
			}
		}
	}
	if current != nil {
		devices = append(devices, *current)
	}
	return devices
}

func parseLsusb(text string) []lsusbDevice {
	lines := strings.Split(text, "\n")
	var devices []lsusbDevice
	re := regexp.MustCompile(`^Bus\s+(\d+)\s+Device\s+(\d+):\s+ID\s+([0-9a-fA-F]{4}):([0-9a-fA-F]{4})\s*(.*)$`)
	for _, raw := range lines {
		line := strings.TrimRight(raw, "\r")
		if line == "" {
			continue
		}
		matches := re.FindStringSubmatch(line)
		if matches == nil {
			continue
		}
		devices = append(devices, lsusbDevice{
			Bus:         matches[1],
			Device:      matches[2],
			VendorID:    matches[3],
			ProductID:   matches[4],
			Description: strings.TrimSpace(matches[5]),
			Raw:         line,
		})
	}
	return devices
}

func splitCSV(value string) []string {
	parts := strings.Split(value, ",")
	out := make([]string, 0, len(parts))
	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part != "" {
			out = append(out, part)
		}
	}
	return out
}

func toInterfaceSlice[T any](items []T) []interface{} {
	out := make([]interface{}, len(items))
	for i, item := range items {
		out[i] = item
	}
	return out
}
