package utils

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

	"skraak_mcp/db"
)

// AudioMothData contains parsed data from AudioMoth comment field
type AudioMothData struct {
	Timestamp  time.Time
	RecorderID string
	Gain       db.GainLevel
	BatteryV   float64
	TempC      float64
}

// AudioMoth comment example:
// "Recorded at 21:00:00 24/02/2025 (UTC+13) by AudioMoth 248AB50153AB0549 at medium gain while battery was 4.3V and temperature was 15.8C."

var (
	// Pattern to detect AudioMoth comments
	audiomothPattern = regexp.MustCompile(`(?i)AudioMoth`)

	// Pattern to extract structured data
	// Matches: "Recorded at HH:MM:SS DD/MM/YYYY (UTC±HH) by AudioMoth HEXID at GAIN gain while battery was X.XV and temperature was Y.YC."
	structuredPattern = regexp.MustCompile(
		`Recorded at (\d{2}:\d{2}:\d{2}) (\d{2}/\d{2}/\d{4}) \(UTC([+-]\d+)\) by AudioMoth ([A-F0-9]+) at ([\w-]+) gain while battery was ([\d.]+)V and temperature was ([-\d.]+)C`,
	)
)

// IsAudioMoth checks if the comment or artist field indicates an AudioMoth recording
func IsAudioMoth(comment, artist string) bool {
	return audiomothPattern.MatchString(comment) || audiomothPattern.MatchString(artist)
}

// ParseAudioMothComment parses structured AudioMoth comment field
// Returns parsed data or error if parsing fails
func ParseAudioMothComment(comment string) (*AudioMothData, error) {
	// Try structured parsing first (newer format)
	if data, err := parseStructuredComment(comment); err == nil {
		return data, nil
	}

	// Fallback to legacy space-separated parsing
	return parseLegacyComment(comment)
}

// parseStructuredComment parses newer AudioMoth comment format using regex
func parseStructuredComment(comment string) (*AudioMothData, error) {
	matches := structuredPattern.FindStringSubmatch(comment)
	if matches == nil {
		return nil, fmt.Errorf("comment does not match structured AudioMoth format")
	}

	// Extract matched groups
	timeStr := matches[1]       // HH:MM:SS
	dateStr := matches[2]       // DD/MM/YYYY
	timezoneStr := matches[3]   // ±HH
	recorderID := matches[4]    // Hex ID
	gainStr := matches[5]       // gain level
	batteryStr := matches[6]    // battery voltage
	tempStr := matches[7]       // temperature

	// Parse timestamp
	timestamp, err := parseAudioMothTimestamp(timeStr, dateStr, timezoneStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse timestamp: %w", err)
	}

	// Parse gain
	gain, err := parseGainLevel(gainStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse gain: %w", err)
	}

	// Parse battery voltage
	batteryV, err := strconv.ParseFloat(batteryStr, 64)
	if err != nil {
		return nil, fmt.Errorf("failed to parse battery voltage: %w", err)
	}

	// Parse temperature
	tempC, err := strconv.ParseFloat(tempStr, 64)
	if err != nil {
		return nil, fmt.Errorf("failed to parse temperature: %w", err)
	}

	return &AudioMothData{
		Timestamp:  timestamp,
		RecorderID: recorderID,
		Gain:       gain,
		BatteryV:   batteryV,
		TempC:      tempC,
	}, nil
}

// parseLegacyComment parses older AudioMoth comment format (space-separated)
// Example: "Recorded at 21:00:00 24/02/2025 (UTC+13) by AudioMoth 248AB50153AB0549 at medium gain while battery was 4.3V and temperature was 15.8C."
func parseLegacyComment(comment string) (*AudioMothData, error) {
	parts := strings.Fields(comment)

	if len(parts) < 10 {
		return nil, fmt.Errorf("comment has insufficient parts (got %d, need at least 10)", len(parts))
	}

	// 0-based indices after split by space:
	// parts[2] = "21:00:00" (time HH:MM:SS)
	// parts[3] = "24/02/2025" (date DD/MM/YYYY)
	// parts[4] = "(UTC+13)" (timezone offset)
	// parts[7] = "248AB50153AB0549" (moth ID)
	// parts[9] = "medium" (gain)
	// parts[len-5] = "4.3V" (battery voltage)
	// parts[len-1] = "15.8C." (temperature)

	timeStr := parts[2]
	dateStr := parts[3]
	timezoneStr := strings.Trim(parts[4], "()")
	recorderID := parts[7]
	gainStr := parts[9]

	// Parse timestamp
	timestamp, err := parseAudioMothTimestamp(timeStr, dateStr, timezoneStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse timestamp: %w", err)
	}

	// Parse gain
	gain, err := parseGainLevel(gainStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse gain: %w", err)
	}

	// Parse battery voltage (e.g., "4.3V")
	batteryStr := parts[len(parts)-5]
	batteryStr = strings.TrimSuffix(batteryStr, "V")
	batteryV, err := strconv.ParseFloat(batteryStr, 64)
	if err != nil {
		return nil, fmt.Errorf("failed to parse battery voltage: %w", err)
	}

	// Parse temperature (e.g., "15.8C." or "15.8C")
	tempStr := parts[len(parts)-1]
	tempStr = strings.TrimSuffix(tempStr, ".")
	tempStr = strings.TrimSuffix(tempStr, "C")
	tempC, err := strconv.ParseFloat(tempStr, 64)
	if err != nil {
		return nil, fmt.Errorf("failed to parse temperature: %w", err)
	}

	return &AudioMothData{
		Timestamp:  timestamp,
		RecorderID: recorderID,
		Gain:       gain,
		BatteryV:   batteryV,
		TempC:      tempC,
	}, nil
}

// parseAudioMothTimestamp parses AudioMoth timestamp from time, date, and timezone strings
// timeStr: "HH:MM:SS"
// dateStr: "DD/MM/YYYY"
// timezoneStr: "UTC+13" or "+13"
func parseAudioMothTimestamp(timeStr, dateStr, timezoneStr string) (time.Time, error) {
	// Parse time components
	timeParts := strings.Split(timeStr, ":")
	if len(timeParts) != 3 {
		return time.Time{}, fmt.Errorf("invalid time format: %s", timeStr)
	}
	hour, _ := strconv.Atoi(timeParts[0])
	minute, _ := strconv.Atoi(timeParts[1])
	second, _ := strconv.Atoi(timeParts[2])

	// Parse date components
	dateParts := strings.Split(dateStr, "/")
	if len(dateParts) != 3 {
		return time.Time{}, fmt.Errorf("invalid date format: %s", dateStr)
	}
	day, _ := strconv.Atoi(dateParts[0])
	month, _ := strconv.Atoi(dateParts[1])
	year, _ := strconv.Atoi(dateParts[2])

	// Parse timezone offset
	timezoneStr = strings.TrimPrefix(timezoneStr, "UTC")
	offsetHours, err := strconv.Atoi(timezoneStr)
	if err != nil {
		return time.Time{}, fmt.Errorf("invalid timezone offset: %s", timezoneStr)
	}

	// Create fixed timezone location
	offsetSeconds := offsetHours * 3600
	loc := time.FixedZone(fmt.Sprintf("UTC%+d", offsetHours), offsetSeconds)

	// Construct timestamp
	timestamp := time.Date(year, time.Month(month), day, hour, minute, second, 0, loc)

	return timestamp, nil
}

// parseGainLevel converts string gain level to GainLevel enum
func parseGainLevel(gainStr string) (db.GainLevel, error) {
	gainStr = strings.ToLower(strings.TrimSpace(gainStr))

	switch gainStr {
	case "low":
		return db.GainLow, nil
	case "low-medium":
		return db.GainLowMedium, nil
	case "medium":
		return db.GainMedium, nil
	case "medium-high":
		return db.GainMediumHigh, nil
	case "high":
		return db.GainHigh, nil
	default:
		return "", fmt.Errorf("unknown gain level: %s", gainStr)
	}
}