package com.storycove.service;

import com.storycove.entity.BackupJob;
import com.storycove.repository.BackupJobRepository;
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.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
public class AsyncBackupService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncBackupService.class);

    @Value("${storycove.upload.dir:/app/images}")
    private String uploadDir;

    @Autowired
    private BackupJobRepository backupJobRepository;

    @Autowired
    private AsyncBackupExecutor asyncBackupExecutor;

    /**
     * Start a backup job asynchronously.
     * This method returns immediately after creating the job record.
     */
    @Transactional
    public BackupJob startBackupJob(String libraryId, BackupJob.BackupType type) {
        logger.info("Creating backup job for library: {}, type: {}", libraryId, type);

        BackupJob job = new BackupJob(libraryId, type);
        job = backupJobRepository.save(job);

        logger.info("Backup job created with ID: {}. Starting async execution...", job.getId());

        // Start backup in background using separate service (ensures @Async works properly)
        asyncBackupExecutor.executeBackupAsync(job.getId());

        logger.info("Async backup execution triggered for job: {}", job.getId());

        return job;
    }

    /**
     * Get backup job status
     */
    public Optional<BackupJob> getJobStatus(UUID jobId) {
        return backupJobRepository.findById(jobId);
    }

    /**
     * Get backup file for download
     */
    public Resource getBackupFile(UUID jobId) throws IOException {
        Optional<BackupJob> jobOpt = backupJobRepository.findById(jobId);
        if (jobOpt.isEmpty()) {
            throw new IOException("Backup job not found");
        }

        BackupJob job = jobOpt.get();

        if (!job.isCompleted()) {
            throw new IOException("Backup is not completed yet");
        }

        if (job.isExpired()) {
            throw new IOException("Backup has expired");
        }

        if (job.getFilePath() == null) {
            throw new IOException("Backup file path not set");
        }

        Path backupPath = Paths.get(job.getFilePath());
        if (!Files.exists(backupPath)) {
            throw new IOException("Backup file not found");
        }

        return new FileSystemResource(backupPath);
    }

    /**
     * List backup jobs for a library
     */
    public List<BackupJob> listBackupJobs(String libraryId) {
        return backupJobRepository.findByLibraryIdOrderByCreatedAtDesc(libraryId);
    }

    /**
     * Clean up expired backup jobs and their files
     * Runs daily at 2 AM
     */
    @Scheduled(cron = "0 0 2 * * ?")
    @Transactional
    public void cleanupExpiredBackups() {
        logger.info("Starting cleanup of expired backups");

        LocalDateTime now = LocalDateTime.now();

        // Mark expired jobs
        int markedCount = backupJobRepository.markExpiredJobs(now);
        logger.info("Marked {} jobs as expired", markedCount);

        // Find all expired jobs to delete their files
        List<BackupJob> expiredJobs = backupJobRepository.findExpiredJobs(now);

        for (BackupJob job : expiredJobs) {
            if (job.getFilePath() != null) {
                try {
                    Path filePath = Paths.get(job.getFilePath());
                    if (Files.exists(filePath)) {
                        Files.delete(filePath);
                        logger.info("Deleted expired backup file: {}", filePath);
                    }
                } catch (IOException e) {
                    logger.warn("Failed to delete expired backup file: {}", job.getFilePath(), e);
                }
            }

            // Delete the job record
            backupJobRepository.delete(job);
        }

        logger.info("Cleanup completed. Deleted {} expired backups", expiredJobs.size());
    }

    /**
     * Delete a specific backup job and its file
     */
    @Transactional
    public void deleteBackupJob(UUID jobId) throws IOException {
        Optional<BackupJob> jobOpt = backupJobRepository.findById(jobId);
        if (jobOpt.isEmpty()) {
            throw new IOException("Backup job not found");
        }

        BackupJob job = jobOpt.get();

        // Delete file if it exists
        if (job.getFilePath() != null) {
            Path filePath = Paths.get(job.getFilePath());
            if (Files.exists(filePath)) {
                Files.delete(filePath);
                logger.info("Deleted backup file: {}", filePath);
            }
        }

        // Delete job record
        backupJobRepository.delete(job);
        logger.info("Deleted backup job: {}", jobId);
    }
}
