// Package nvidia_bug_report provides parser for NVIDIA bug report files
// Generated by nvidia-bug-report.sh script
package nvidia_bug_report

import (
	"fmt"
	"regexp"
	"strings"
	"time"

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

// parserVersion - version of this parser module
const parserVersion = "1.2"

var bugReportDateLineRegex = regexp.MustCompile(`(?m)^Date:\s+(.+?)\s*$`)
var dateWithTZAbbrevRegex = regexp.MustCompile(`^([A-Za-z]{3}\s+[A-Za-z]{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+([A-Za-z]{2,5})\s+(\d{4})$`)

var timezoneAbbrevToOffset = map[string]string{
	"UTC": "+00:00",
	"GMT": "+00:00",
	"EST": "-05:00",
	"EDT": "-04:00",
	"CST": "-06:00",
	"CDT": "-05:00",
	"MST": "-07:00",
	"MDT": "-06:00",
	"PST": "-08:00",
	"PDT": "-07:00",
}

func init() {
	parser.Register(&Parser{})
}

// Parser implements VendorParser for NVIDIA bug reports
type Parser struct{}

// Name returns human-readable parser name
func (p *Parser) Name() string {
	return "NVIDIA Bug Report Parser"
}

// Vendor returns vendor identifier
func (p *Parser) Vendor() string {
	return "nvidia_bug_report"
}

// Version returns parser version
func (p *Parser) Version() string {
	return parserVersion
}

// Detect checks if this is an NVIDIA bug report
// Returns confidence 0-100
func (p *Parser) Detect(files []parser.ExtractedFile) int {
	// Only detect if there's exactly one file
	if len(files) != 1 {
		return 0
	}

	file := files[0]

	// Check filename
	if !strings.Contains(strings.ToLower(file.Path), "nvidia-bug-report") {
		return 0
	}

	// Check content markers
	content := string(file.Content)
	if !strings.Contains(content, "nvidia-bug-report.sh") ||
		!strings.Contains(content, "NVIDIA bug report log file") {
		return 0
	}

	// High confidence for nvidia-bug-report files
	return 85
}

// Parse parses NVIDIA bug report file
func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, error) {
	result := &models.AnalysisResult{
		Events:  make([]models.Event, 0),
		FRU:     make([]models.FRUInfo, 0),
		Sensors: make([]models.SensorReading, 0),
	}

	// Initialize hardware config
	result.Hardware = &models.HardwareConfig{
		CPUs:        make([]models.CPU, 0),
		Memory:      make([]models.MemoryDIMM, 0),
		GPUs:        make([]models.GPU, 0),
		PowerSupply: make([]models.PSU, 0),
	}

	if len(files) == 0 {
		return result, nil
	}

	content := string(files[0].Content)
	if collectedAt, tzOffset, ok := parseBugReportCollectedAt(content); ok {
		result.CollectedAt = collectedAt.UTC()
		result.SourceTimezone = tzOffset
	}

	// Parse system information
	parseSystemInfo(content, result)

	// Parse CPU information
	parseCPUInfo(content, result)

	// Parse memory modules
	parseMemoryModules(content, result)

	// Parse power supplies
	parsePSUInfo(content, result)

	// Parse GPU information
	parseGPUInfo(content, result)

	// Parse network adapters
	parseNetworkAdapters(content, result)

	// Parse driver version
	parseDriverVersion(content, result)

	return result, nil
}

func parseBugReportCollectedAt(content string) (time.Time, string, bool) {
	matches := bugReportDateLineRegex.FindStringSubmatch(content)
	if len(matches) != 2 {
		return time.Time{}, "", false
	}
	raw := strings.TrimSpace(matches[1])
	if raw == "" {
		return time.Time{}, "", false
	}

	if m := dateWithTZAbbrevRegex.FindStringSubmatch(raw); len(m) == 4 {
		if offset, ok := timezoneAbbrevToOffset[strings.ToUpper(strings.TrimSpace(m[2]))]; ok {
			layout := "Mon Jan 2 15:04:05 -07:00 2006"
			normalized := strings.TrimSpace(m[1]) + " " + offset + " " + strings.TrimSpace(m[3])
			if ts, err := time.Parse(layout, normalized); err == nil {
				return ts, offset, true
			}
		}
	}

	layouts := []string{
		"Mon Jan 2 15:04:05 MST 2006",
		"Mon Jan 2 15:04:05 2006",
	}
	for _, layout := range layouts {
		ts, err := time.Parse(layout, raw)
		if err != nil {
			continue
		}
		return ts, formatOffset(ts), true
	}
	return time.Time{}, "", false
}

func formatOffset(t time.Time) string {
	_, sec := t.Zone()
	sign := '+'
	if sec < 0 {
		sign = '-'
		sec = -sec
	}
	h := sec / 3600
	m := (sec % 3600) / 60
	return fmt.Sprintf("%c%02d:%02d", sign, h, m)
}
