package main

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"
)

const pkgMaxResults = 50

// detectDistro lee /etc/os-release y devuelve ID (ubuntu, debian, fedora, arch, ...).
func detectDistro() string {
	f, err := os.Open("/etc/os-release")
	if err != nil {
		return ""
	}
	defer f.Close()
	sc := bufio.NewScanner(f)
	for sc.Scan() {
		line := sc.Text()
		if strings.HasPrefix(line, "ID=") {
			return strings.Trim(strings.TrimPrefix(line, "ID="), `"`)
		}
	}
	return ""
}

// runPkgSearch busca paquetes. Detect distro y delega a apt/dnf/pacman.
// Permite override via env PKG_FAKE_OUTPUT (for tests).
func runPkgSearch(cap *Capability, args map[string]any) (any, int, error) {
	_ = cap
	query := mapStringField(args, "query")
	if query == "" {
		return nil, -1, fmt.Errorf("query required")
	}
	// Validar query: solo alfanumerico + . + _ + -
	for _, r := range query {
		if !(r >= 'a' && r <= 'z') && !(r >= 'A' && r <= 'Z') &&
			!(r >= '0' && r <= '9') && r != '.' && r != '_' && r != '-' && r != '+' {
			return nil, -1, fmt.Errorf("invalid char in query: %q", query)
		}
	}

	// Test fake output via env
	if fake := os.Getenv("PKG_FAKE_OUTPUT"); fake != "" {
		return parsePkgOutput(fake, "apt", query), 0, nil
	}

	distro := detectDistro()
	var bin string
	var argv []string
	switch distro {
	case "ubuntu", "debian":
		bin = "apt-cache"
		argv = []string{"search", query}
	case "fedora", "rhel", "centos":
		bin = "dnf"
		argv = []string{"search", "--quiet", query}
	case "arch", "manjaro":
		bin = "pacman"
		argv = []string{"-Ss", query}
	default:
		// fallback try apt-cache
		if _, err := exec.LookPath("apt-cache"); err == nil {
			bin = "apt-cache"
			argv = []string{"search", query}
			distro = "ubuntu"
		} else {
			return nil, -1, fmt.Errorf("unsupported distro %q (no pkg manager found)", distro)
		}
	}

	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	c := exec.CommandContext(ctx, bin, argv...) // #nosec G204 — query validated
	out, err := c.Output()
	if err != nil {
		return nil, -1, fmt.Errorf("%s search: %w", bin, err)
	}
	return parsePkgOutput(string(out), distro, query), 0, nil
}

// parsePkgOutput parsea salida apt-cache search / dnf search / pacman -Ss.
func parsePkgOutput(out, distro, query string) map[string]any {
	packages := []map[string]any{}
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		var name, desc string
		switch distro {
		case "ubuntu", "debian", "apt":
			// "name - description"
			idx := strings.Index(line, " - ")
			if idx > 0 {
				name = line[:idx]
				desc = line[idx+3:]
			} else {
				name = line
			}
		case "arch", "manjaro":
			// "repo/name version" then next line "    desc"
			if strings.HasPrefix(line, "    ") {
				if len(packages) > 0 {
					packages[len(packages)-1]["description"] = strings.TrimSpace(line)
				}
				continue
			}
			parts := strings.Fields(line)
			if len(parts) > 0 {
				name = parts[0]
			}
		default:
			// dnf: "name.arch : description"
			if idx := strings.Index(line, " : "); idx > 0 {
				name = strings.TrimSpace(line[:idx])
				desc = strings.TrimSpace(line[idx+3:])
			} else {
				name = line
			}
		}
		if name == "" {
			continue
		}
		packages = append(packages, map[string]any{
			"name":        name,
			"description": desc,
		})
		if len(packages) >= pkgMaxResults {
			break
		}
	}
	return map[string]any{
		"query":     query,
		"distro":    distro,
		"packages":  packages,
		"count":     len(packages),
		"truncated": len(packages) >= pkgMaxResults,
	}
}
