package exporter

import (
	"encoding/csv"
	"encoding/json"
	"io"
	"strings"

	"git.mchus.pro/mchus/logpile/internal/models"
)

// Exporter handles data export in various formats
type Exporter struct {
	result *models.AnalysisResult
}

// New creates a new exporter
func New(result *models.AnalysisResult) *Exporter {
	return &Exporter{result: result}
}

// ExportCSV exports serial numbers to CSV format
func (e *Exporter) ExportCSV(w io.Writer) error {
	writer := csv.NewWriter(w)
	defer writer.Flush()

	// Header
	if err := writer.Write([]string{"Component", "Serial Number", "Manufacturer", "Location"}); err != nil {
		return err
	}

	if e.result == nil {
		return nil
	}

	// FRU data
	for _, fru := range e.result.FRU {
		if !hasUsableSerial(fru.SerialNumber) {
			continue
		}
		name := fru.ProductName
		if name == "" {
			name = fru.Description
		}
		if err := writer.Write([]string{
			name,
			fru.SerialNumber,
			fru.Manufacturer,
			fru.PartNumber,
		}); err != nil {
			return err
		}
	}

	// Hardware data
	if e.result.Hardware != nil {
		// Board
		if hasUsableSerial(e.result.Hardware.BoardInfo.SerialNumber) {
			if err := writer.Write([]string{
				e.result.Hardware.BoardInfo.ProductName,
				strings.TrimSpace(e.result.Hardware.BoardInfo.SerialNumber),
				e.result.Hardware.BoardInfo.Manufacturer,
				"Board",
			}); err != nil {
				return err
			}
		}

		// CPUs
		for _, cpu := range e.result.Hardware.CPUs {
			if !hasUsableSerial(cpu.SerialNumber) {
				continue
			}
			if err := writer.Write([]string{
				cpu.Model,
				strings.TrimSpace(cpu.SerialNumber),
				"",
				"CPU",
			}); err != nil {
				return err
			}
		}

		// Memory
		for _, mem := range e.result.Hardware.Memory {
			if !hasUsableSerial(mem.SerialNumber) {
				continue
			}
			location := mem.Location
			if location == "" {
				location = mem.Slot
			}
			if err := writer.Write([]string{
				mem.PartNumber,
				strings.TrimSpace(mem.SerialNumber),
				mem.Manufacturer,
				location,
			}); err != nil {
				return err
			}
		}

		// Storage
		for _, stor := range e.result.Hardware.Storage {
			if !hasUsableSerial(stor.SerialNumber) {
				continue
			}
			if err := writer.Write([]string{
				stor.Model,
				strings.TrimSpace(stor.SerialNumber),
				stor.Manufacturer,
				stor.Slot,
			}); err != nil {
				return err
			}
		}

		// GPUs
		for _, gpu := range e.result.Hardware.GPUs {
			if !hasUsableSerial(gpu.SerialNumber) {
				continue
			}
			component := gpu.Model
			if component == "" {
				component = "GPU"
			}
			if err := writer.Write([]string{
				component,
				strings.TrimSpace(gpu.SerialNumber),
				gpu.Manufacturer,
				gpu.Slot,
			}); err != nil {
				return err
			}
		}

		// PCIe devices
		for _, pcie := range e.result.Hardware.PCIeDevices {
			if !hasUsableSerial(pcie.SerialNumber) {
				continue
			}
			if err := writer.Write([]string{
				pcie.DeviceClass,
				strings.TrimSpace(pcie.SerialNumber),
				pcie.Manufacturer,
				pcie.Slot,
			}); err != nil {
				return err
			}
		}

		// Network adapters
		for _, nic := range e.result.Hardware.NetworkAdapters {
			if !hasUsableSerial(nic.SerialNumber) {
				continue
			}
			location := nic.Location
			if location == "" {
				location = nic.Slot
			}
			if err := writer.Write([]string{
				nic.Model,
				strings.TrimSpace(nic.SerialNumber),
				nic.Vendor,
				location,
			}); err != nil {
				return err
			}
		}

		// Legacy network cards
		for _, nic := range e.result.Hardware.NetworkCards {
			if !hasUsableSerial(nic.SerialNumber) {
				continue
			}
			if err := writer.Write([]string{
				nic.Model,
				strings.TrimSpace(nic.SerialNumber),
				"",
				"Network",
			}); err != nil {
				return err
			}
		}

		// Power supplies
		for _, psu := range e.result.Hardware.PowerSupply {
			if !hasUsableSerial(psu.SerialNumber) {
				continue
			}
			if err := writer.Write([]string{
				psu.Model,
				strings.TrimSpace(psu.SerialNumber),
				psu.Vendor,
				psu.Slot,
			}); err != nil {
				return err
			}
		}
	}

	return nil
}

// ExportJSON exports all data to JSON format
func (e *Exporter) ExportJSON(w io.Writer) error {
	encoder := json.NewEncoder(w)
	encoder.SetIndent("", "  ")
	return encoder.Encode(e.result)
}

func hasUsableSerial(serial string) bool {
	s := strings.TrimSpace(serial)
	if s == "" {
		return false
	}
	switch strings.ToUpper(s) {
	case "N/A", "NA", "NONE", "NULL", "UNKNOWN", "-":
		return false
	default:
		return true
	}
}
