using System;
using Tornado3_2026Election.Domain;

namespace Tornado3_2026Election.Services;

public enum ThumbnailDisplayContext
{
    CutList,
    Queue,
    Preview,
    PlaybackPreview
}

public readonly record struct ThumbnailDisplayMetrics(double Width, double Height);

public static class ThumbnailLayoutResolver
{
    private const double HdAspectRatio = 1920d / 1080d;
    private const double VideoWall3840x810AspectRatio = 3840d / 810d;
    private const double VideoWall2880x1080AspectRatio = 2880d / 1080d;
    private const double VideoWall8316x1080AspectRatio = 8316d / 1080d;

    public static ThumbnailDisplayMetrics ResolveDisplayMetrics(
        FormatTemplateDefinition template,
        VideoWallLayoutPreset videoWallLayoutPreset,
        ThumbnailDisplayContext context)
    {
        return ResolveDisplayMetrics(template.RecommendedChannel, template.SceneWidth, template.SceneHeight, videoWallLayoutPreset, context);
    }

    public static ThumbnailDisplayMetrics ResolveDisplayMetrics(
        BroadcastChannel channel,
        VideoWallLayoutPreset videoWallLayoutPreset,
        ThumbnailDisplayContext context)
    {
        return ResolveDisplayMetrics(channel, null, null, videoWallLayoutPreset, context);
    }

    public static (int Width, int Height) ResolveGenerationSize(
        FormatTemplateDefinition template,
        VideoWallLayoutPreset videoWallLayoutPreset)
    {
        const int thumbnailHeight = 180;
        var aspectRatio = ResolveAspectRatio(template.RecommendedChannel, template.SceneWidth, template.SceneHeight, videoWallLayoutPreset);
        var thumbnailWidth = Math.Max(1, (int)Math.Round(thumbnailHeight * aspectRatio, MidpointRounding.AwayFromZero));
        return (thumbnailWidth, thumbnailHeight);
    }

    private static ThumbnailDisplayMetrics ResolveDisplayMetrics(
        BroadcastChannel channel,
        int? sceneWidth,
        int? sceneHeight,
        VideoWallLayoutPreset videoWallLayoutPreset,
        ThumbnailDisplayContext context)
    {
        var aspectRatio = ResolveAspectRatio(channel, sceneWidth, sceneHeight, videoWallLayoutPreset);
        var (maxWidth, maxHeight) = context switch
        {
            ThumbnailDisplayContext.Preview => (480d, 180d),
            ThumbnailDisplayContext.PlaybackPreview => (220d, 124d),
            ThumbnailDisplayContext.CutList => (320d, 90d),
            ThumbnailDisplayContext.Queue => (320d, 90d),
            _ => (320d, 180d)
        };

        return FitWithin(aspectRatio, maxWidth, maxHeight);
    }

    private static double ResolveAspectRatio(
        BroadcastChannel channel,
        int? sceneWidth,
        int? sceneHeight,
        VideoWallLayoutPreset videoWallLayoutPreset)
    {
        if (channel == BroadcastChannel.VideoWall)
        {
            if (TryGetPresetAspectRatio(videoWallLayoutPreset, out var presetAspectRatio))
            {
                return presetAspectRatio;
            }

            if (TryGetSceneAspectRatio(sceneWidth, sceneHeight, out var sceneAspectRatio))
            {
                return sceneAspectRatio;
            }

            return VideoWall8316x1080AspectRatio;
        }

        if (TryGetSceneAspectRatio(sceneWidth, sceneHeight, out var resolvedSceneAspectRatio))
        {
            return resolvedSceneAspectRatio;
        }

        return HdAspectRatio;
    }

    private static bool TryGetPresetAspectRatio(VideoWallLayoutPreset videoWallLayoutPreset, out double aspectRatio)
    {
        switch (videoWallLayoutPreset)
        {
            case VideoWallLayoutPreset.Wall3840x810:
                aspectRatio = VideoWall3840x810AspectRatio;
                return true;
            case VideoWallLayoutPreset.Wall2880x1080:
                aspectRatio = VideoWall2880x1080AspectRatio;
                return true;
            case VideoWallLayoutPreset.UltraWide8316x1080:
                aspectRatio = VideoWall8316x1080AspectRatio;
                return true;
            default:
                aspectRatio = 0;
                return false;
        }
    }

    private static bool TryGetSceneAspectRatio(int? sceneWidth, int? sceneHeight, out double aspectRatio)
    {
        if (sceneWidth is > 0 && sceneHeight is > 0)
        {
            aspectRatio = (double)sceneWidth.Value / sceneHeight.Value;
            return true;
        }

        aspectRatio = 0;
        return false;
    }

    private static ThumbnailDisplayMetrics FitWithin(double aspectRatio, double maxWidth, double maxHeight)
    {
        var width = maxWidth;
        var height = width / aspectRatio;
        if (height > maxHeight)
        {
            height = maxHeight;
            width = height * aspectRatio;
        }

        return new ThumbnailDisplayMetrics(
            Math.Max(1, Math.Round(width, 0, MidpointRounding.AwayFromZero)),
            Math.Max(1, Math.Round(height, 0, MidpointRounding.AwayFromZero)));
    }
}
