package parser

import (
	"fmt"
	"io"
	"strings"
	"time"

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

// BMCParser parses BMC diagnostic archives using vendor-specific parsers
type BMCParser struct {
	result       *models.AnalysisResult
	files        []ExtractedFile
	vendorParser VendorParser
}

// NewBMCParser creates a new BMC parser
func NewBMCParser() *BMCParser {
	return &BMCParser{
		result: &models.AnalysisResult{
			Events:  make([]models.Event, 0),
			FRU:     make([]models.FRUInfo, 0),
			Sensors: make([]models.SensorReading, 0),
		},
	}
}

// ParseArchive parses an archive file
func (p *BMCParser) ParseArchive(archivePath string) error {
	files, err := ExtractArchive(archivePath)
	if err != nil {
		return fmt.Errorf("extract archive: %w", err)
	}
	p.files = files
	return p.parseFiles()
}

// ParseFromReader parses archive from reader
func (p *BMCParser) ParseFromReader(r io.Reader, filename string) error {
	files, err := ExtractArchiveFromReader(r, filename)
	if err != nil {
		return fmt.Errorf("extract archive: %w", err)
	}
	p.files = files
	p.result.Filename = filename
	return p.parseFiles()
}

func (p *BMCParser) parseFiles() error {
	// Auto-detect format
	vendorParser, err := DetectFormat(p.files)
	if err != nil {
		return fmt.Errorf("detect format: %w", err)
	}
	p.vendorParser = vendorParser

	// Parse using detected vendor parser
	result, err := vendorParser.Parse(p.files)
	if err != nil {
		return fmt.Errorf("parse with %s: %w", vendorParser.Name(), err)
	}

	// Preserve filename
	result.Filename = p.result.Filename

	appendExtractionWarnings(result, p.files)
	if result.CollectedAt.IsZero() {
		if ts := inferCollectedAtFromExtractedFiles(p.files); !ts.IsZero() {
			result.CollectedAt = ts.UTC()
		}
	}
	p.result = result

	return nil
}

func inferCollectedAtFromExtractedFiles(files []ExtractedFile) time.Time {
	var latestReliable time.Time
	var latestAny time.Time
	for _, f := range files {
		ts := f.ModTime
		if ts.IsZero() {
			continue
		}
		if latestAny.IsZero() || ts.After(latestAny) {
			latestAny = ts
		}
		// Ignore placeholder archive mtimes like 1980-01-01.
		if ts.Year() < 2000 {
			continue
		}
		if latestReliable.IsZero() || ts.After(latestReliable) {
			latestReliable = ts
		}
	}
	if !latestReliable.IsZero() {
		return latestReliable
	}
	return latestAny
}

func appendExtractionWarnings(result *models.AnalysisResult, files []ExtractedFile) {
	if result == nil {
		return
	}

	truncated := make([]string, 0)
	for _, f := range files {
		if !f.Truncated {
			continue
		}
		if f.TruncatedMessage != "" {
			truncated = append(truncated, fmt.Sprintf("%s: %s", f.Path, f.TruncatedMessage))
			continue
		}
		truncated = append(truncated, fmt.Sprintf("%s: content was truncated due to size limit", f.Path))
	}

	if len(truncated) == 0 {
		return
	}

	result.Events = append(result.Events, models.Event{
		Timestamp:   time.Now(),
		Source:      "LOGPile",
		EventType:   "Analysis Warning",
		Severity:    models.SeverityWarning,
		Description: "Input data was too large; analysis is partial and may be incomplete",
		RawData:     strings.Join(truncated, "; "),
	})
}

// Result returns the analysis result
func (p *BMCParser) Result() *models.AnalysisResult {
	return p.result
}

// GetFiles returns all extracted files
func (p *BMCParser) GetFiles() []ExtractedFile {
	return p.files
}

// DetectedVendor returns the detected vendor parser name
func (p *BMCParser) DetectedVendor() string {
	if p.vendorParser != nil {
		return p.vendorParser.Name()
	}
	return "unknown"
}
