using PlotLine.Data;
using PlotLine.Models;
using PlotLine.ViewModels;

namespace PlotLine.Services;

public interface IProjectRestorePointService
{
    Task<ProjectRestorePointListViewModel?> GetAsync(int projectId);
    Task<ProjectRestorePoint?> CreateManualAsync(int projectId, string? restorePointName, string? notes);
    Task<ProjectRestorePoint?> CreatePreRestoreAsync(int projectId, string sourceRestorePointName);
    Task<ProjectRestorePointDownload?> DownloadAsync(int projectRestorePointId, int projectId);
    Task<bool> HasReachedManualLimitAsync(int projectId);
    Task<bool> DeleteAsync(int projectRestorePointId, int projectId);
}

public sealed class ProjectRestorePointService(
    IProjectRepository projects,
    IProjectExportService projectExports,
    IProjectRestorePointRepository restorePoints,
    ILogger<ProjectRestorePointService> logger) : IProjectRestorePointService
{
    private const string ManualRestorePointType = "Manual";
    private const string PreRestorePointType = "PreRestore";
    private const int MaxRestorePointsPerProject = 10;
    public const string LimitReachedMessage = "You have reached the limit of 10 restore points for this project. Delete an old restore point before creating a new one. You can download a restore point first if you want to keep a local backup copy.";

    public async Task<ProjectRestorePointListViewModel?> GetAsync(int projectId)
    {
        var project = await projects.GetAsync(projectId);
        if (project is null)
        {
            return null;
        }

        return new ProjectRestorePointListViewModel
        {
            Project = project,
            RestorePoints = await restorePoints.ListAsync(projectId)
        };
    }

    public async Task<ProjectRestorePoint?> CreateManualAsync(int projectId, string? restorePointName, string? notes)
    {
        if (await HasReachedManualLimitAsync(projectId))
        {
            logger.LogInformation("Manual restore point creation blocked for project {ProjectID} because the project already has {MaxRestorePointCount} restore points.",
                projectId,
                MaxRestorePointsPerProject);
            return null;
        }

        var name = string.IsNullOrWhiteSpace(restorePointName)
            ? $"Manual restore point - {DateTime.Now:yyyy-MM-dd HH:mm}"
            : restorePointName.Trim();

        return await CreateAsync(projectId, name, ManualRestorePointType, Clean(notes));
    }

    public async Task<ProjectRestorePoint?> CreatePreRestoreAsync(int projectId, string sourceRestorePointName)
    {
        var name = $"Automatic Pre-Restore Backup - {DateTime.Now:yyyy-MM-dd HH:mm}";
        var notes = $"Created automatically before restoring from \"{sourceRestorePointName}\".";
        return await CreateAsync(projectId, name, PreRestorePointType, notes);
    }

    private async Task<ProjectRestorePoint?> CreateAsync(int projectId, string restorePointName, string restorePointType, string? notes)
    {
        var export = await projectExports.ExportAsync(projectId);
        if (export is null)
        {
            logger.LogWarning("{RestorePointType} restore point requested for missing project {ProjectID}.", restorePointType, projectId);
            return null;
        }

        var created = await restorePoints.CreateAsync(projectId, restorePointName, restorePointType, export.Json, notes);
        logger.LogInformation("{RestorePointType} restore point {ProjectRestorePointID} created for project {ProjectID} with {JsonSizeBytes} JSON bytes.",
            restorePointType,
            created.ProjectRestorePointID,
            projectId,
            created.JsonSizeBytes);

        return created;
    }

    public async Task<ProjectRestorePointDownload?> DownloadAsync(int projectRestorePointId, int projectId)
    {
        var restorePoint = await restorePoints.GetAsync(projectRestorePointId, projectId);
        if (restorePoint?.JsonData is null)
        {
            logger.LogWarning("Restore point download requested for missing restore point {ProjectRestorePointID} in project {ProjectID}.", projectRestorePointId, projectId);
            return null;
        }

        return new ProjectRestorePointDownload
        {
            FileName = BuildFileName(restorePoint.ProjectName, restorePoint.CreatedDateUtc),
            ContentType = "application/json",
            Json = restorePoint.JsonData
        };
    }

    public async Task<bool> HasReachedManualLimitAsync(int projectId)
        => await restorePoints.CountAsync(projectId) >= MaxRestorePointsPerProject;

    public async Task<bool> DeleteAsync(int projectRestorePointId, int projectId)
    {
        var deleted = await restorePoints.DeleteAsync(projectRestorePointId, projectId);
        if (!deleted)
        {
            logger.LogWarning("Restore point delete failed for restore point {ProjectRestorePointID} in project {ProjectID}.", projectRestorePointId, projectId);
        }

        return deleted;
    }

    private static string? Clean(string? value)
    {
        return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
    }

    private static string BuildFileName(string projectName, DateTime createdDateUtc)
    {
        var safeTitle = new string(projectName
            .Select(c => Path.GetInvalidFileNameChars().Contains(c) ? '_' : c)
            .ToArray())
            .Trim();

        safeTitle = string.Join("_", safeTitle.Split(' ', StringSplitOptions.RemoveEmptyEntries));

        if (string.IsNullOrWhiteSpace(safeTitle))
        {
            safeTitle = "PlotDirector_Project";
        }

        return $"{safeTitle}_RestorePoint_{createdDateUtc:yyyy-MM-dd_HHmm}.plotdirector";
    }
}
