package logger

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

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

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

func (r *Retention) Start() {
	// Запускаем проверку каждые 15 минут
	r.StartWithInterval(15 * time.Minute)
}

func (r *Retention) StartWithInterval(interval time.Duration) {
	// Запускаем проверку с указанным интервалом
	ticker := time.NewTicker(interval)
	go func() {
		for {
			select {
			case <-ticker.C:
				r.Cleanup()
			case <-r.stopCh:
				ticker.Stop()
				return
			}
		}
	}()

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

func (r *Retention) Stop() {
	close(r.stopCh)
}

func (r *Retention) Cleanup() {
	// Если оба лимита отключены, ничего не делаем
	if r.maxAgeHours <= 0 && r.maxSizeBytes <= 0 {
		return
	}

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

	// 1. Удаляем старые файлы (если включено)
	deletedByAge := 0
	var deletedByAgeBytes int64
	if r.maxAgeHours > 0 {
		deletedByAge, deletedByAgeBytes = r.cleanByAge(dataDir)
	}

	// 2. Проверяем общий размер и удаляем самые старые если превышен лимит (если включено)
	deletedBySize := 0
	var deletedBySizeBytes int64
	if r.maxSizeBytes > 0 {
		deletedBySize, deletedBySizeBytes = r.cleanBySize(dataDir)
	}

	totalDeleted := deletedByAge + deletedBySize
	totalDeletedBytes := deletedByAgeBytes + deletedBySizeBytes

	// Логируем ТОЛЬКО если были удаления
	if totalDeleted > 0 {
		// Логируем суммарное событие
		if r.eventLogger != nil {
			eventMsg := fmt.Sprintf("ОЧИСТКА_ЛОГОВ_УДАЛЕНО_%d_ФАЙЛОВ_%d_MB",
				totalDeleted, totalDeletedBytes/(1024*1024))
			r.eventLogger.Event(eventMsg)
		}

		// Пишем в консоль для отладки
		log.Printf("Очистка логов: удалено %d файлов (%.2f MB) [по возрасту: %d, по размеру: %d]",
			totalDeleted, float64(totalDeletedBytes)/(1024*1024), deletedByAge, deletedBySize)
	}
}

// cleanByAge возвращает количество удаленных файлов и их общий размер в байтах
func (r *Retention) cleanByAge(dataDir string) (int, int64) {
	cutoff := time.Now().Add(-time.Duration(r.maxAgeHours) * time.Hour)
	deleted := 0
	var deletedBytes int64

	// Получаем все .bin файлы
	files, err := filepath.Glob(filepath.Join(dataDir, "gpio-*.bin"))
	if err != nil {
		return 0, 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++
				deletedBytes += info.Size()
			}
		}
	}

	return deleted, deletedBytes
}

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

	if len(files) == 0 {
		return 0, 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
	var deletedBytes int64

	// Сохраняем как минимум 1 файл
	for totalSize > r.maxSizeBytes && len(fileList) > 1 {
		// Удаляем самый старый файл
		oldest := fileList[0]
		if err := os.Remove(oldest.path); err == nil {
			totalSize -= oldest.size
			deletedBytes += oldest.size
			deleted++
			fileList = fileList[1:]
		} else {
			break
		}
	}

	return deleted, deletedBytes
}

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)
}