package com.storycove.service;

import com.storycove.repository.StoryRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Service for automatic daily backups.
 * Runs at 4 AM daily and creates a backup if content has changed since last backup.
 * Keeps maximum of MAX_BACKUPS auto backups, rotating old ones out.
 */
@Service
public class AutomaticBackupService {

    private static final Logger logger = LoggerFactory.getLogger(AutomaticBackupService.class);
    private static final int MAX_BACKUPS = 10;
    private static final DateTimeFormatter FILENAME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");

    @Value("${storycove.backup.dir:/app/backups}")
    private String backupDir;

    @Autowired
    private StoryRepository storyRepository;

    @Autowired
    private DatabaseManagementService databaseManagementService;

    @Autowired
    private LibraryService libraryService;

    private LocalDateTime lastBackupCheck = null;

    /**
     * Scheduled job that runs daily at 4 AM.
     * Creates a backup if content has changed since last backup.
     */
    @Scheduled(cron = "0 0 4 * * ?")
    public void performAutomaticBackup() {
        logger.info("========================================");
        logger.info("Starting automatic backup check at 4 AM");
        logger.info("========================================");

        try {
            String libraryId = libraryService.getCurrentLibraryId();
            if (libraryId == null) {
                libraryId = "default";
            }

            logger.info("Checking for content changes in library: {}", libraryId);

            if (!hasContentChanged()) {
                logger.info("No content changes detected since last backup. Skipping backup.");
                logger.info("========================================");
                return;
            }

            logger.info("Content changes detected! Creating automatic backup...");

            Path libraryBackupDir = Paths.get(backupDir, libraryId);
            Files.createDirectories(libraryBackupDir);

            String timestamp = LocalDateTime.now().format(FILENAME_FORMATTER);
            String filename = String.format("auto_backup_%s.zip", timestamp);
            Path backupFile = libraryBackupDir.resolve(filename);

            logger.info("Creating complete backup to: {}", backupFile);

            Resource backup = databaseManagementService.createCompleteBackup();

            try (var inputStream = backup.getInputStream();
                 var outputStream = Files.newOutputStream(backupFile)) {
                inputStream.transferTo(outputStream);
            }

            long fileSize = Files.size(backupFile);
            logger.info("Automatic backup created successfully");
            logger.info("   File: {}", backupFile.getFileName());
            logger.info("   Size: {} MB", fileSize / 1024 / 1024);

            rotateAutoBackups(libraryBackupDir);

            lastBackupCheck = LocalDateTime.now();

            logger.info("========================================");
            logger.info("Automatic backup completed successfully");
            logger.info("========================================");

        } catch (Exception e) {
            logger.error("Automatic backup failed", e);
            logger.info("========================================");
        }
    }

    private boolean hasContentChanged() {
        try {
            if (lastBackupCheck == null) {
                long storyCount = storyRepository.count();
                logger.info("First backup check - found {} stories", storyCount);
                return storyCount > 0;
            }

            long changedCount = storyRepository.countStoriesModifiedAfter(lastBackupCheck);
            logger.info("Found {} stories modified since last backup ({})", changedCount, lastBackupCheck);
            return changedCount > 0;

        } catch (Exception e) {
            logger.error("Error checking for content changes", e);
            return true;
        }
    }

    /**
     * Rotate auto backups — keep only MAX_BACKUPS most recent auto_backup_*.zip files.
     * Manual backups are never touched by rotation.
     */
    private void rotateAutoBackups(Path libraryBackupDir) throws IOException {
        logger.info("Checking for old automatic backups to rotate...");

        List<Path> autoBackupFiles;
        try (Stream<Path> stream = Files.list(libraryBackupDir)) {
            autoBackupFiles = stream
                    .filter(Files::isRegularFile)
                    .filter(p -> p.getFileName().toString().startsWith("auto_backup_"))
                    .filter(p -> p.getFileName().toString().endsWith(".zip"))
                    .sorted(Comparator.comparing((Path p) -> {
                        try {
                            return Files.getLastModifiedTime(p);
                        } catch (IOException e) {
                            return null;
                        }
                    }).reversed())
                    .collect(Collectors.toList());
        }

        logger.info("Found {} automatic backups", autoBackupFiles.size());

        if (autoBackupFiles.size() > MAX_BACKUPS) {
            List<Path> toDelete = autoBackupFiles.subList(MAX_BACKUPS, autoBackupFiles.size());
            logger.info("Deleting {} old automatic backups to maintain maximum of {}", toDelete.size(), MAX_BACKUPS);

            for (Path oldBackup : toDelete) {
                try {
                    Files.delete(oldBackup);
                    logger.info("   Deleted old backup: {}", oldBackup.getFileName());
                } catch (IOException e) {
                    logger.warn("Failed to delete old backup: {}", oldBackup, e);
                }
            }
        } else {
            logger.info("Auto backup count within limit ({}), no rotation needed", MAX_BACKUPS);
        }
    }

    public void triggerManualBackup() {
        logger.info("Manual automatic backup triggered");
        performAutomaticBackup();
    }
}
