using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using NexusShell.Models;

namespace NexusShell.Services;

public enum PlatformUpdateJobPhase
{
    None,
    Preparing,
    Applying,
    Failed,
    Blocked,
    Completed,
}

public sealed class NexusPlatformUpdateJob
{
    public PlatformUpdateJobPhase Phase { get; set; }
    public string? ZipPath { get; set; }
    public string? InstallRoot { get; set; }
    public int? TargetBuild { get; set; }
    public DateTimeOffset StartedUtc { get; set; }
    public DateTimeOffset UpdatedUtc { get; set; }
    public string? ErrorMessage { get; set; }
    public string? LockedFile { get; set; }
}

/// <summary>
/// Persists platform update job state for timeout and stale recovery across restarts.
/// </summary>
public static class NexusPlatformUpdateJobStore
{
    public static TimeSpan PreparingTimeout { get; } = TimeSpan.FromMinutes(3);
    public static TimeSpan ApplyingTimeout { get; } = TimeSpan.FromMinutes(10);

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        WriteIndented = true,
        Converters = { new JsonStringEnumConverter() },
    };

    private static string JobFilePath =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "NinjaNexus",
            "update",
            "pending-job.json");

    public static NexusPlatformUpdateJob? Load()
    {
        try
        {
            if (!File.Exists(JobFilePath))
            {
                return null;
            }

            var json = File.ReadAllText(JobFilePath);
            return JsonSerializer.Deserialize<NexusPlatformUpdateJob>(json, JsonOptions);
        }
        catch
        {
            return null;
        }
    }

    public static void Save(NexusPlatformUpdateJob job)
    {
        job.UpdatedUtc = DateTimeOffset.UtcNow;
        var dir = Path.GetDirectoryName(JobFilePath)!;
        Directory.CreateDirectory(dir);
        File.WriteAllText(JobFilePath, JsonSerializer.Serialize(job, JsonOptions));
    }

    public static void Clear()
    {
        try
        {
            if (File.Exists(JobFilePath))
            {
                File.Delete(JobFilePath);
            }
        }
        catch
        {
            // Best effort.
        }
    }

    public static ClientUpdateFlowState? RecoverStaleJob(out string? errorMessage)
    {
        errorMessage = null;
        var job = Load();
        if (job is null || job.Phase is PlatformUpdateJobPhase.None or PlatformUpdateJobPhase.Completed)
        {
            return null;
        }

        var age = DateTimeOffset.UtcNow - job.UpdatedUtc;
        var timedOut = job.Phase switch
        {
            PlatformUpdateJobPhase.Preparing => age > PreparingTimeout,
            PlatformUpdateJobPhase.Applying => age > ApplyingTimeout,
            _ => false,
        };

        if (!timedOut && job.Phase is not PlatformUpdateJobPhase.Failed and not PlatformUpdateJobPhase.Blocked)
        {
            return null;
        }

        if (timedOut)
        {
            errorMessage = job.Phase switch
            {
                PlatformUpdateJobPhase.Preparing =>
                    "Opdatering hang i 'forbereder installation'. Timeout — prøv igen.",
                PlatformUpdateJobPhase.Applying =>
                    "Opdatering hang under anvendelse. Timeout — prøv igen.",
                _ => job.ErrorMessage ?? "Opdatering fejlede.",
            };

            job.Phase = PlatformUpdateJobPhase.Failed;
            job.ErrorMessage = errorMessage;
            TrySaveRecoveredJob(job);
            UpdateLogWriter.Error($"Stale update job recovered: {errorMessage}");
            return ClientUpdateFlowState.Stale;
        }

        if (job.Phase is PlatformUpdateJobPhase.Failed or PlatformUpdateJobPhase.Blocked)
        {
            errorMessage = job.ErrorMessage;
            return job.Phase == PlatformUpdateJobPhase.Blocked
                ? ClientUpdateFlowState.Blocked
                : ClientUpdateFlowState.Failed;
        }

        return null;
    }

    private static void TrySaveRecoveredJob(NexusPlatformUpdateJob job)
    {
        try
        {
            Save(job);
        }
        catch (IOException ex)
        {
            UpdateLogWriter.Error($"Could not persist recovered update job: {ex.Message}");
        }
        catch (UnauthorizedAccessException ex)
        {
            UpdateLogWriter.Error($"Could not persist recovered update job: {ex.Message}");
        }
    }

    public static void MarkCompleted()
    {
        Clear();
    }
}
