using Dalamud.Plugin;
using LightlessSync.Interop.Ipc.Framework;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
using Penumbra.Api.Enums;
using Penumbra.Api.IpcSubscribers;

namespace LightlessSync.Interop.Ipc.Penumbra;

public sealed class PenumbraTexture : PenumbraBase
{
    private readonly PenumbraRedraw _redrawFeature;
    private readonly ConvertTextureFile _convertTextureFile;

    public PenumbraTexture(
        ILogger logger,
        IDalamudPluginInterface pluginInterface,
        DalamudUtilService dalamudUtil,
        LightlessMediator mediator,
        PenumbraRedraw redrawFeature) : base(logger, pluginInterface, dalamudUtil, mediator)
    {
        _redrawFeature = redrawFeature;
        _convertTextureFile = new ConvertTextureFile(pluginInterface);
    }

    public override string Name => "Penumbra.Textures";

    public async Task ConvertTextureFilesAsync(ILogger logger, IReadOnlyList<TextureConversionJob> jobs, IProgress<TextureConversionProgress>? progress, CancellationToken token)
    {
        if (!IsAvailable || jobs.Count == 0)
        {
            return;
        }

        Mediator.Publish(new HaltScanMessage(nameof(ConvertTextureFilesAsync)));

        var totalJobs = jobs.Count;
        var completedJobs = 0;

        try
        {
            foreach (var job in jobs)
            {
                if (token.IsCancellationRequested)
                {
                    break;
                }

                progress?.Report(new TextureConversionProgress(completedJobs, totalJobs, job));
                await ConvertSingleJobAsync(logger, job, token).ConfigureAwait(false);
                completedJobs++;
            }
        }
        finally
        {
            Mediator.Publish(new ResumeScanMessage(nameof(ConvertTextureFilesAsync)));
        }

        if (completedJobs > 0 && !token.IsCancellationRequested)
        {
            await DalamudUtil.RunOnFrameworkThread(async () =>
            {
                var player = await DalamudUtil.GetPlayerPointerAsync().ConfigureAwait(false);
                if (player == null)
                {
                    return;
                }

                var gameObject = await DalamudUtil.CreateGameObjectAsync(player).ConfigureAwait(false);
                if (gameObject == null)
                {
                    return;
                }

                _redrawFeature.RequestImmediateRedraw(gameObject.ObjectIndex, RedrawType.Redraw);
            }).ConfigureAwait(false);
        }
    }

    public async Task ConvertTextureFileDirectAsync(TextureConversionJob job, CancellationToken token)
    {
        if (!IsAvailable)
        {
            return;
        }

        await ConvertSingleJobAsync(Logger, job, token).ConfigureAwait(false);
    }

    private async Task ConvertSingleJobAsync(ILogger logger, TextureConversionJob job, CancellationToken token)
    {
        token.ThrowIfCancellationRequested();

        logger.LogInformation("Converting texture {Input} -> {Output} ({Target})", job.InputFile, job.OutputFile, job.TargetType);
        var convertTask = _convertTextureFile.Invoke(job.InputFile, job.OutputFile, job.TargetType, job.IncludeMipMaps);
        await convertTask.ConfigureAwait(false);

        if (!convertTask.IsCompletedSuccessfully || job.DuplicateTargets is not { Count: > 0 })
        {
            return;
        }

        foreach (var duplicate in job.DuplicateTargets)
        {
            try
            {
                logger.LogInformation("Synchronizing duplicate {Duplicate}", duplicate);
                File.Copy(job.OutputFile, duplicate, overwrite: true);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Failed to copy duplicate {Duplicate}", duplicate);
            }
        }
    }

    protected override void HandleStateChange(IpcConnectionState previous, IpcConnectionState current)
    {
    }
}
