using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using JobTrackerApi.Data;
using JobTrackerApi.Models;

namespace JobTrackerApi.Controllers
{
    [ApiController]
    [Route("api/backup")]
    [Authorize(AuthenticationSchemes = "local")]
    public class BackupController : ControllerBase
    {
        private readonly JobTrackerContext _db;
        private readonly ILogger<BackupController> _logger;
        private readonly IDataProtector _protector;

        public BackupController(JobTrackerContext db, ILogger<BackupController> logger, IDataProtectionProvider dp)
        {
            _db = db;
            _logger = logger;
            _protector = dp.CreateProtector("JobTrackerApi.Backup.v1");
        }

        public sealed record BackupEnvelope(
            string Version,
            DateTime CreatedAt,
            object Data
        );

        [HttpPost("encrypted")]
        public async Task<IActionResult> Encrypted(CancellationToken cancellationToken)
        {
            var data = await BuildExport(cancellationToken);
            var envelope = new BackupEnvelope("jtbackup.v1", DateTime.Now, data);

            var jsonBytes = JsonSerializer.SerializeToUtf8Bytes(
                envelope,
                new JsonSerializerOptions { WriteIndented = true }
            );

            // Data Protection encrypts payload using the app's key ring.
            // On Windows, keys are encrypted at rest for the current user.
            var protectedText = _protector.Protect(Convert.ToBase64String(jsonBytes));
            var cipher = Encoding.UTF8.GetBytes(protectedText);
            var fileName = $"jobtracker_backup_{DateTime.Now:yyyyMMdd_HHmmss}.jtbackup";

            _logger.LogInformation("Generated encrypted backup {FileName} ({Bytes} bytes).", fileName, cipher.Length);

            return File(cipher, "application/octet-stream", fileName);
        }

        private async Task<object> BuildExport(CancellationToken cancellationToken)
        {
            // Avoid navigation cycles by exporting as plain graphs / DTOs.
            var companies = await _db.Companies.AsNoTracking().OrderBy(c => c.Name).ToListAsync(cancellationToken);
            var jobs = await _db.JobApplications.AsNoTracking().OrderByDescending(j => j.DateApplied).ToListAsync(cancellationToken);
            var jobIds = jobs.Select(j => j.Id).ToList();

            var correspondence = await _db.Correspondences.AsNoTracking()
                .Where(c => jobIds.Contains(c.JobApplicationId))
                .OrderBy(c => c.Date)
                .ToListAsync(cancellationToken);

            var attachments = await _db.Attachments.AsNoTracking()
                .Where(a => jobIds.Contains(a.JobApplicationId))
                .OrderBy(a => a.UploadDate)
                .ToListAsync(cancellationToken);

            var events = await _db.JobEvents.AsNoTracking()
                .Where(e => jobIds.Contains(e.JobApplicationId))
                .OrderBy(e => e.At)
                .ToListAsync(cancellationToken);
            var emailSendAttempts = await _db.EmailSendAttempts.AsNoTracking()
                .Where(attempt => jobIds.Contains(attempt.JobApplicationId))
                .OrderBy(attempt => attempt.CreatedAtUtc)
                .Select(attempt => new EmailSendAttemptExport(
                    attempt.Id,
                    attempt.JobApplicationId,
                    attempt.Provider,
                    attempt.ClientRequestId,
                    attempt.Status,
                    attempt.ProviderMessageId,
                    attempt.FailureCategory,
                    attempt.CreatedAtUtc,
                    attempt.StartedAtUtc,
                    attempt.CompletedAtUtc))
                .ToListAsync(cancellationToken);
            var emailDrafts = await _db.EmailDrafts.AsNoTracking()
                .Where(draft => jobIds.Contains(draft.JobApplicationId))
                .OrderBy(draft => draft.UpdatedAtUtc)
                .Select(draft => new EmailDraftExport(
                    draft.Id,
                    draft.JobApplicationId,
                    draft.Provider,
                    draft.To,
                    draft.Subject,
                    draft.BodyText,
                    draft.ThreadId,
                    draft.ClientRequestId,
                    draft.Revision,
                    draft.CreatedAtUtc,
                    draft.UpdatedAtUtc))
                .ToListAsync(cancellationToken);
            var rules = await _db.RuleSettings.AsNoTracking().FirstOrDefaultAsync(cancellationToken);

            return new
            {
                Companies = companies,
                JobApplications = jobs,
                Correspondence = correspondence,
                Attachments = attachments,
                Events = events,
                EmailSendAttempts = emailSendAttempts,
                EmailDrafts = emailDrafts,
                Rules = rules
            };
        }
    }
}
