package transfer

import (
	"context"
	"fmt"
	"log"
	"os"
	"sync"
	"time"
)

type CleanupService struct {
	config       CleanupConfig
	pendingFiles []string
	mu           sync.Mutex
}

func NewCleanupService(config CleanupConfig) *CleanupService {
	return &CleanupService{
		config:       config,
		pendingFiles: make([]string, 0),
	}
}

func (cs *CleanupService) ScheduleCleanup(filePath string) error {
	if !cs.config.Enabled {
		return nil
	}

	cs.mu.Lock()
	defer cs.mu.Unlock()
	cs.pendingFiles = append(cs.pendingFiles, filePath)
	log.Printf("Scheduled file for cleanup: %s", filePath)
	return nil
}

func (cs *CleanupService) Start(ctx context.Context) error {
	if !cs.config.Enabled {
		log.Println("Cleanup service disabled")
		return nil
	}

	log.Printf("Cleanup service started (retention: %v, batch: %d)", cs.config.RetentionPeriod, cs.config.BatchSize)

	ticker := time.NewTicker(cs.config.CheckInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			log.Println("Cleanup service shutting down...")
			return ctx.Err()
		case <-ticker.C:
			if err := cs.ExecuteCleanup(ctx); err != nil {
				log.Printf("Cleanup error: %v", err)
			}
		}
	}
}

func (cs *CleanupService) ExecuteCleanup(ctx context.Context) error {
	cs.mu.Lock()
	if len(cs.pendingFiles) == 0 {
		cs.mu.Unlock()
		return nil
	}

	batchSize := cs.config.BatchSize
	if batchSize > len(cs.pendingFiles) {
		batchSize = len(cs.pendingFiles)
	}

	log.Printf("Executing cleanup batch (size: %d)", batchSize)

	batch := make([]string, batchSize)
	copy(batch, cs.pendingFiles[:batchSize])
	cs.pendingFiles = cs.pendingFiles[batchSize:]
	cs.mu.Unlock()

	log.Printf("Processing %d files for cleanup", len(batch))

	var cleanedCount int
	var errors []error

	for _, filePath := range batch {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		if err := cs.cleanupFile(filePath); err != nil {
			errors = append(errors, fmt.Errorf("Failed to cleanup file %s: %w", filePath, err))
		} else {
			cleanedCount++
		}
	}

	log.Printf("Cleanup batch completed (cleaned: %d, errors: %d)", cleanedCount, len(errors))

	if len(errors) > 0 {
		for i, err := range errors {
			if i >= 3 {
				log.Printf("... and %d more errors", len(errors)-3)
				break
			}
			log.Printf("Error: %v", err)
		}
	}

	return nil

}

func (cs *CleanupService) cleanupFile(filePath string) error {
	info, err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("Failed to get file info: %w", err)
	}

	if cs.config.RetentionPeriod > 0 {
		if time.Since(info.ModTime()) < cs.config.RetentionPeriod {
			log.Printf("File too new to cleanup: %s", filePath)
			return nil
		}
	}

	if err := os.Remove(filePath); err != nil {
		return fmt.Errorf("Failed to remove file: %w", err)
	}

	log.Printf("File cleaned up: %s", filePath)
	return nil
}

func (cs *CleanupService) GetPendingCount() int {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	return len(cs.pendingFiles)
}

func (cs *CleanupService) ForceCleanupAll(ctx context.Context) error {
	log.Println("Force cleanup requested")

	for {
		cs.mu.Lock()
		pendingCount := len(cs.pendingFiles)
		cs.mu.Unlock()

		if pendingCount == 0 {
			break
		}

		if err := cs.ExecuteCleanup(ctx); err != nil {
			return err
		}

		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-time.After(100 * time.Millisecond):
		}
	}

	log.Println("Force cleanup complete")
	return nil
}
