package logger

import (
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type Retention struct {
	maxAgeHours  int
	maxSizeBytes int64
	eventLogger  *EventLogger
}

func NewRetention(maxAgeHours int, maxSizeMB int, eventLogger *EventLogger) *Retention {
	return &Retention{
		maxAgeHours:  maxAgeHours,
		maxSizeBytes: int64(maxSizeMB) * 1024 * 1024,
		eventLogger:  eventLogger,
	}
}

func (r *Retention) Start() {
	// Запускаем проверку каждые 15 минут
	ticker := time.NewTicker(15 * time.Minute)
	go func() {
		for range ticker.C {
			r.Cleanup()
		}
	}()

	// Первая очистка через 1 минуту после запуска
	time.AfterFunc(1*time.Minute, func() {
		r.Cleanup()
	})
}

func (r *Retention) Cleanup() {
	// Получаем директорию с данными
	dataDir, err := GetDataLogsDir()
	if err != nil {
		if r.eventLogger != nil {
			r.eventLogger.Event("ОШИБКА_ПОЛУЧЕНИЯ_ДИРЕКТОРИИ")
		}
		return
	}

	if r.eventLogger != nil {
		r.eventLogger.Event("ЗАПУЩЕНА_ОЧИСТКА_ЛОГОВ")
	}

	// 1. Удаляем старые файлы
	deletedByAge := r.cleanByAge(dataDir)

	// 2. Проверяем общий размер и удаляем самые старые если превышен лимит
	deletedBySize := r.cleanBySize(dataDir)

	if r.eventLogger != nil && (deletedByAge > 0 || deletedBySize > 0) {
		r.eventLogger.Event("УДАЛЕНО_ФАЙЛОВ")
		log.Printf("Очистка логов: удалено %d файлов (по возрасту: %d, по размеру: %d)",
			deletedByAge+deletedBySize, deletedByAge, deletedBySize)
	}
}

func (r *Retention) cleanByAge(dataDir string) int {
	cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour)
	deleted := 0

	// Получаем все .bin файлы
	files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
	if err != nil {
		return 0
	}

	for _, file := range files {
		info, err := os.Stat(file)
		if err != nil {
			continue
		}

		// Извлекаем время из имени файла
		fileTime := r.parseFilenameTime(file)
		if fileTime.IsZero() {
			fileTime = info.ModTime()
		}

		if fileTime.Before(cutoff) {
			if err := os.Remove(file); err == nil {
				deleted++
				if r.eventLogger != nil {
					r.eventLogger.Event("УДАЛЕН_ФАЙЛ_ПО_ВОЗРАСТУ")
				}
			}
		}
	}

	return deleted
}

func (r *Retention) cleanBySize(dataDir string) int {
	// Получаем все файлы
	files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
	if err != nil {
		return 0
	}

	if len(files) == 0 {
		return 0
	}

	// Сортируем по времени (старыми первыми)
	type fileInfo struct {
		path string
		time time.Time
		size int64
	}

	var fileList []fileInfo
	var totalSize int64

	for _, file := range files {
		info, err := os.Stat(file)
		if err != nil {
			continue
		}

		fileTime := r.parseFilenameTime(file)
		if fileTime.IsZero() {
			fileTime = info.ModTime()
		}

		fileList = append(fileList, fileInfo{
			path: file,
			time: fileTime,
			size: info.Size(),
		})
		totalSize += info.Size()
	}

	// Сортируем по времени (старые первые)
	sort.Slice(fileList, func(i, j int) bool {
		return fileList[i].time.Before(fileList[j].time)
	})

	// Удаляем самые старые пока не уложимся в лимит
	deleted := 0
	for totalSize > r.maxSizeBytes && len(fileList) > 1 {
		// Удаляем самый старый файл
		oldest := fileList[0]
		if err := os.Remove(oldest.path); err == nil {
			totalSize -= oldest.size
			deleted++
			fileList = fileList[1:]
			if r.eventLogger != nil {
				r.eventLogger.Event("УДАЛЕН_ФАЙЛ_ПО_РАЗМЕРУ")
			}
		} else {
			break
		}
	}

	return deleted
}

func (r *Retention) parseFilenameTime(filename string) time.Time {
	base := filepath.Base(filename)
	// Формат: gpio-2026-06-09-11.bin
	parts := strings.Split(strings.TrimSuffix(base, ".bin"), "-")
	if len(parts) != 5 {
		return time.Time{}
	}

	year, _ := time.Parse("2006", parts[1])
	month, _ := time.Parse("01", parts[2])
	day, _ := time.Parse("02", parts[3])
	hour, _ := time.Parse("15", parts[4])

	return time.Date(year.Year(), month.Month(), day.Day(), hour.Hour(), 0, 0, 0, time.Local)
}