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 5 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.automatic-backup.dir:/app/automatic-backups}")
    private String automaticBackupDir;

    @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 {
            // Get current library ID (or default)
            String libraryId = libraryService.getCurrentLibraryId();
            if (libraryId == null) {
                libraryId = "default";
            }

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

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

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

            // Create backup directory for this library
            Path backupPath = Paths.get(automaticBackupDir, libraryId);
            Files.createDirectories(backupPath);

            // Create the backup
            String timestamp = LocalDateTime.now().format(FILENAME_FORMATTER);
            String filename = String.format("auto_backup_%s.zip", timestamp);
            Path backupFile = backupPath.resolve(filename);

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

            Resource backup = databaseManagementService.createCompleteBackup();

            // Write backup to file
            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);

            // Rotate old backups (keep only MAX_BACKUPS)
            rotateBackups(backupPath);

            // Update last backup check time
            lastBackupCheck = LocalDateTime.now();

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

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

    /**
     * Check if content has changed since last backup.
     * Looks for stories created or updated after the last backup time.
     */
    private boolean hasContentChanged() {
        try {
            if (lastBackupCheck == null) {
                // First run - check if there are any stories at all
                long storyCount = storyRepository.count();
                logger.info("First backup check - found {} stories", storyCount);
                return storyCount > 0;
            }

            // Check for stories created or updated since last backup
            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);
            // On error, create backup to be safe
            return true;
        }
    }

    /**
     * Rotate backups - keep only MAX_BACKUPS most recent backups.
     * Deletes older backups.
     */
    private void rotateBackups(Path backupPath) throws IOException {
        logger.info("Checking for old backups to rotate...");

        // Find all backup files in the directory
        List<Path> backupFiles;
        try (Stream<Path> stream = Files.list(backupPath)) {
            backupFiles = 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()) // Most recent first
                    .collect(Collectors.toList());
        }

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

        // Delete old backups if we exceed MAX_BACKUPS
        if (backupFiles.size() > MAX_BACKUPS) {
            List<Path> toDelete = backupFiles.subList(MAX_BACKUPS, backupFiles.size());
            logger.info("Deleting {} old 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("Backup count within limit ({}), no rotation needed", MAX_BACKUPS);
        }
    }

    /**
     * Manual trigger for testing - creates backup immediately if content changed.
     */
    public void triggerManualBackup() {
        logger.info("Manual automatic backup triggered");
        performAutomaticBackup();
    }

    /**
     * Get list of automatic backups for the current library.
     */
    public List<BackupInfo> listAutomaticBackups() throws IOException {
        String libraryId = libraryService.getCurrentLibraryId();
        if (libraryId == null) {
            libraryId = "default";
        }

        Path backupPath = Paths.get(automaticBackupDir, libraryId);
        if (!Files.exists(backupPath)) {
            return List.of();
        }

        try (Stream<Path> stream = Files.list(backupPath)) {
            return 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())
                    .map(p -> {
                        try {
                            return new BackupInfo(
                                    p.getFileName().toString(),
                                    Files.size(p),
                                    Files.getLastModifiedTime(p).toInstant().toString()
                            );
                        } catch (IOException e) {
                            return null;
                        }
                    })
                    .filter(info -> info != null)
                    .collect(Collectors.toList());
        }
    }

    /**
     * Simple backup info class.
     */
    public static class BackupInfo {
        private final String filename;
        private final long sizeBytes;
        private final String createdAt;

        public BackupInfo(String filename, long sizeBytes, String createdAt) {
            this.filename = filename;
            this.sizeBytes = sizeBytes;
            this.createdAt = createdAt;
        }

        public String getFilename() {
            return filename;
        }

        public long getSizeBytes() {
            return sizeBytes;
        }

        public String getCreatedAt() {
            return createdAt;
        }
    }
}
