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.Resource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * Separate service for async backup execution.
 * This is needed because @Async doesn't work when called from within the same class.
 */
@Service
public class AsyncBackupExecutor {

    private static final Logger logger = LoggerFactory.getLogger(AsyncBackupExecutor.class);
    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 BackupJobRepository backupJobRepository;

    @Autowired
    private DatabaseManagementService databaseManagementService;

    @Autowired
    private LibraryService libraryService;

    /**
     * Execute backup asynchronously.
     * This method MUST be in a separate service class for @Async to work properly.
     */
    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void executeBackupAsync(UUID jobId) {
        logger.info("Async executor starting for job {}", jobId);

        Optional<BackupJob> jobOpt = backupJobRepository.findById(jobId);
        if (jobOpt.isEmpty()) {
            logger.error("Backup job not found: {}", jobId);
            return;
        }

        BackupJob job = jobOpt.get();
        job.setStatus(BackupJob.BackupStatus.IN_PROGRESS);
        job.setStartedAt(LocalDateTime.now());
        job.setProgressPercent(0);
        backupJobRepository.save(job);

        try {
            logger.info("Starting backup job {} for library {}", job.getId(), job.getLibraryId());

            // Switch to the correct library
            if (!job.getLibraryId().equals(libraryService.getCurrentLibraryId())) {
                libraryService.switchToLibraryAfterAuthentication(job.getLibraryId());
            }

            // Create backup file in the unified backup directory
            Path libraryBackupDir = Paths.get(backupDir, job.getLibraryId());
            Files.createDirectories(libraryBackupDir);

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

            job.setProgressPercent(10);
            backupJobRepository.save(job);

            // Create the backup
            Resource backupResource;
            if (job.getType() == BackupJob.BackupType.COMPLETE) {
                backupResource = databaseManagementService.createCompleteBackup();
            } else {
                backupResource = databaseManagementService.createBackup();
            }

            job.setProgressPercent(80);
            backupJobRepository.save(job);

            // Write backup to file
            try (var inputStream = backupResource.getInputStream();
                 var outputStream = Files.newOutputStream(backupFile)) {
                inputStream.transferTo(outputStream);
            }

            job.setProgressPercent(95);
            backupJobRepository.save(job);

            // Set file info
            job.setFilePath(backupFile.toString());
            job.setFileSizeBytes(Files.size(backupFile));
            job.setStatus(BackupJob.BackupStatus.COMPLETED);
            job.setCompletedAt(LocalDateTime.now());
            job.setProgressPercent(100);

            logger.info("Backup job {} completed successfully. File: {}, Size: {} bytes",
                    job.getId(), filename, job.getFileSizeBytes());

        } catch (Exception e) {
            logger.error("Backup job {} failed", job.getId(), e);
            job.setStatus(BackupJob.BackupStatus.FAILED);
            job.setErrorMessage(e.getMessage());
            job.setCompletedAt(LocalDateTime.now());
        } finally {
            backupJobRepository.save(job);
        }
    }
}
