using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using UnityEngine;

public partial class OctopusSDK
{
    public static void SetTheme(OctopusColorScheme colorScheme = null, OctopusColorScheme lightColorScheme = null, OctopusColorScheme darkColorScheme = null, OctopusLogo logo = null, bool navBarUsesPrimaryColor = false, OctopusFonts fonts = null, string appName = "")
    {
        SetLightColorScheme(lightColorScheme ?? colorScheme);
        SetDarkColorScheme(darkColorScheme ?? colorScheme);
        SetLogo(logo);
        SetNavBarUsesPrimaryColor(navBarUsesPrimaryColor);
        SetFonts(fonts);
        SetAppName(appName);
    }

    public static void SetLightColorScheme(OctopusColorScheme colorScheme)
    {
#if UNITY_EDITOR
        MockBackend.SetLightColorScheme(colorScheme);
#else
        if (colorScheme != null)
        {
#if UNITY_ANDROID
            using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
            {
                plugin.CallStatic(
                    "setLightColorScheme",
                    ColorToInt(colorScheme.Primary),
                    ColorToInt(colorScheme.PrimaryLow),
                    ColorToInt(colorScheme.PrimaryHigh),
                    ColorToInt(colorScheme.OnPrimary),
                    ColorToInt(colorScheme.Link),
                    ColorToInt(colorScheme.Background)
                );
            }
#elif UNITY_IOS
            OctopusSdkSetLightColorScheme(
                ColorToInt(colorScheme.Primary),
                ColorToInt(colorScheme.PrimaryLow),
                ColorToInt(colorScheme.PrimaryHigh),
                ColorToInt(colorScheme.OnPrimary),
                ColorToInt(colorScheme.Link),
                ColorToInt(colorScheme.Background)
            );
#endif
        }
#endif
    }

    public static void SetDarkColorScheme(OctopusColorScheme colorScheme)
    {
#if UNITY_EDITOR
        MockBackend.SetDarkColorScheme(colorScheme);
#else
        if (colorScheme != null)
        {
#if UNITY_ANDROID
            using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
            {
                plugin.CallStatic(
                    "setDarkColorScheme",
                    ColorToInt(colorScheme.Primary),
                    ColorToInt(colorScheme.PrimaryLow),
                    ColorToInt(colorScheme.PrimaryHigh),
                    ColorToInt(colorScheme.OnPrimary),
                    ColorToInt(colorScheme.Link),
                    ColorToInt(colorScheme.Background)
                );
            }
#elif UNITY_IOS
            OctopusSdkSetDarkColorScheme(
                ColorToInt(colorScheme.Primary),
                ColorToInt(colorScheme.PrimaryLow),
                ColorToInt(colorScheme.PrimaryHigh),
                ColorToInt(colorScheme.OnPrimary),
                ColorToInt(colorScheme.Link),
                ColorToInt(colorScheme.Background)
            );
#endif
        }
#endif
    }

    public static void SetLogo(OctopusLogo logo)
    {
#if UNITY_EDITOR
        MockBackend.SetLogo();
#elif UNITY_ANDROID
        using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
        {
            plugin.CallStatic("setLogo", logo?.AndroidDrawableName ?? "");
        }
#elif UNITY_IOS
        OctopusSdkSetLogo(logo?.IOSResourceName ?? "");
#endif
    }

    public static void SetAppName(string appName)
    {
#if UNITY_EDITOR
        MockBackend.SetAppName(appName);
#elif UNITY_ANDROID
        using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
        {
            plugin.CallStatic("setAppName", appName);
        }
#elif UNITY_IOS
        OctopusSdkSetAppName(appName);
#endif
    }

    public static void SetNavBarUsesPrimaryColor(bool usesPrimary)
    {
#if UNITY_EDITOR
        MockBackend.SetNavBarUsesPrimaryColor(usesPrimary);
#elif UNITY_ANDROID
        using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
        {
            plugin.CallStatic("setNavBarUsesPrimaryColor", usesPrimary);
        }
#elif UNITY_IOS
        OctopusSdkSetNavBarUsesPrimaryColor(usesPrimary);
#endif
    }

    /// <summary>
    /// Sets the color scheme type: 0 = System, 1 = Light, 2 = Dark
    /// </summary>
    public static void SetColorSchemeType(int colorSchemeType)
    {
#if UNITY_EDITOR
        MockBackend.SetColorSchemeType(colorSchemeType);
#elif UNITY_ANDROID
        using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
        {
            plugin.CallStatic("setColorSchemeType", colorSchemeType);
        }
#elif UNITY_IOS
        OctopusSdkSetColorSchemeType(colorSchemeType);
#endif
    }

    /// <summary>
    /// Forces the native Octopus community UI to a fixed orientation, independent of the game.
    /// 0 = None (follow the game/device), 1 = Portrait, 2 = Landscape.
    /// </summary>
    public static void SetForcedOrientation(int forcedOrientation)
    {
#if UNITY_EDITOR
        MockBackend.SetForcedOrientation(forcedOrientation);
#elif UNITY_ANDROID
        using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
        {
            plugin.CallStatic("setForcedOrientation", forcedOrientation);
        }
#elif UNITY_IOS
        OctopusSdkSetForcedOrientation(forcedOrientation);
#endif
    }

    public static void SetFonts(OctopusFonts fonts = null)
    {
#if UNITY_EDITOR
        MockBackend.SetFonts(fonts);
#elif UNITY_ANDROID
        using (AndroidJavaClass plugin = new AndroidJavaClass("com.octopuscommunity.bridge.Bridge"))
        {
            plugin.CallStatic("setFonts",
                fonts?.Title1?.AndroidFontResourceName ?? "",
                fonts?.Title1?.Size ?? 0f,
                fonts?.Title2?.AndroidFontResourceName ?? "",
                fonts?.Title2?.Size ?? 0f,
                fonts?.Body1?.AndroidFontResourceName ?? "",
                fonts?.Body1?.Size ?? 0f,
                fonts?.Body2?.AndroidFontResourceName ?? "",
                fonts?.Body2?.Size ?? 0f,
                fonts?.Caption1?.AndroidFontResourceName ?? "",
                fonts?.Caption1?.Size ?? 0f,
                fonts?.Caption2?.AndroidFontResourceName ?? "",
                fonts?.Caption2?.Size ?? 0f,
                fonts?.NavBarItem?.AndroidFontResourceName ?? "",
                fonts?.NavBarItem?.Size ?? 0f
            );
        }
#elif UNITY_IOS
        OctopusSdkSetFonts(
            fonts?.Title1?.IOSFontName ?? "",
            fonts?.Title1?.Size ?? 0f,
            fonts?.Title2?.IOSFontName ?? "",
            fonts?.Title2?.Size ?? 0f,
            fonts?.Body1?.IOSFontName ?? "",
            fonts?.Body1?.Size ?? 0f,
            fonts?.Body2?.IOSFontName ?? "",
            fonts?.Body2?.Size ?? 0f,
            fonts?.Caption1?.IOSFontName ?? "",
            fonts?.Caption1?.Size ?? 0f,
            fonts?.Caption2?.IOSFontName ?? "",
            fonts?.Caption2?.Size ?? 0f,
            fonts?.NavBarItem?.IOSFontName ?? "",
            fonts?.NavBarItem?.Size ?? 0f
        );
#endif
        SetFontWeights(fonts);
    }

    public static int ColorToInt(Color color)
    {
        Color32 c = color;
        return ((int)c.a << 24) |
               ((int)c.r << 16) |
               ((int)c.g << 8) |
               (int)c.b;
    }

#if UNITY_IOS && !UNITY_EDITOR
    // Any of the six carries 0 (fully transparent) when the host left it unset; the Swift side
    // maps that to nil so the native default applies.
    [DllImport("__Internal")]
    private static extern void OctopusSdkSetLightColorScheme(int primary, int primaryLow, int primaryHigh, int onPrimary, int link, int background);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetDarkColorScheme(int primary, int primaryLow, int primaryHigh, int onPrimary, int link, int background);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetLogo(string logo);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetAppName(string appName);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetNavBarUsesPrimaryColor(bool usesPrimary);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetColorSchemeType(int colorSchemeType);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetForcedOrientation(int forcedOrientation);

    [DllImport("__Internal")]
    private static extern void OctopusSdkSetFonts(
        string title1Font, float title1Size,
        string title2Font, float title2Size,
        string body1Font, float body1Size,
        string body2Font, float body2Size,
        string caption1Font, float caption1Size,
        string caption2Font, float caption2Size,
        string navBarItemFont, float navBarItemSize
    );
#endif 
}

/// <summary>
/// Colors applied to the native Octopus community UI.
/// </summary>
/// <remarks>
/// <see cref="Link"/> and <see cref="Background"/> are optional: leaving one at
/// <c>Color.clear</c> (fully transparent, the value the constructors default to) means
/// "not set", and the native SDK keeps its own default for that slot. No default value is
/// duplicated on the C# side.
/// </remarks>
public class OctopusColorScheme
{
    // Every color below is applied on its own: Color.clear (the packed int 0) crosses the bridge
    // as "not set" and the native SDK keeps its own default for that slot.

    /// <summary>Primary brand color. <c>Color.clear</c> keeps the native default.</summary>
    public readonly Color Primary;

    /// <summary>
    /// Low contrast variant of the primary color. <c>Color.clear</c> keeps the native default.
    /// </summary>
    public readonly Color PrimaryLow;

    /// <summary>
    /// High contrast variant of the primary color. <c>Color.clear</c> keeps the native default.
    /// </summary>
    public readonly Color PrimaryHigh;

    /// <summary>
    /// Color of the content displayed over a surface painted with the primary color.
    /// <c>Color.clear</c> keeps the native default.
    /// </summary>
    public readonly Color OnPrimary;

    /// <summary>
    /// Color of links (URLs displayed in posts and comments).
    /// <c>Color.clear</c> keeps the native default.
    /// </summary>
    public readonly Color Link;

    /// <summary>
    /// Background color of the community screens.
    /// <c>Color.clear</c> keeps the native default (the system background on iOS, the
    /// scheme's <c>gray100</c> on Android).
    /// </summary>
    public readonly Color Background;

    /// <summary>
    /// Creates a color scheme that customizes the primary color set only; the native SDK keeps
    /// its own defaults for <see cref="Link"/> and <see cref="Background"/>.
    /// </summary>
    /// <remarks>
    /// Kept as its own constructor rather than folded into the one below: an assembly compiled
    /// against an earlier version of this package binds to this exact four-argument signature.
    /// </remarks>
    public OctopusColorScheme(
        Color primary,
        Color primaryLow,
        Color primaryHigh,
        Color onPrimary
    ) : this(primary, primaryLow, primaryHigh, onPrimary, Color.clear, Color.clear)
    {
    }

    /// <summary>
    /// Creates a color scheme. <paramref name="link"/> and <paramref name="background"/> are
    /// optional — omit one, or pass <c>Color.clear</c>, to keep the native default. The same
    /// applies to the four colors of the primary set: each one passed as <c>Color.clear</c> is
    /// left to the native SDK rather than painted transparent.
    /// </summary>
    public OctopusColorScheme(
        Color primary,
        Color primaryLow,
        Color primaryHigh,
        Color onPrimary,
        Color link = default(Color),
        Color background = default(Color)
    )
    {
        this.Primary = primary;
        this.PrimaryLow = primaryLow;
        this.PrimaryHigh = primaryHigh;
        this.OnPrimary = onPrimary;
        this.Link = link;
        this.Background = background;
    }
}

public class OctopusLogo
{
    public readonly string AndroidDrawableName;
    public readonly string IOSResourceName;

    public OctopusLogo(string androidDrawableName, string iOSResourceName)
    {
        AndroidDrawableName = androidDrawableName;
        IOSResourceName = iOSResourceName;
    }
}

public class OctopusFonts
{
    public readonly OctopusFont Title1;
    public readonly OctopusFont Title2;
    public readonly OctopusFont Body1;
    public readonly OctopusFont Body2;
    public readonly OctopusFont Caption1;
    public readonly OctopusFont Caption2;
    public readonly OctopusFont NavBarItem;

    public OctopusFonts(
        OctopusFont title1 = null,
        OctopusFont title2 = null,
        OctopusFont body1 = null,
        OctopusFont body2 = null,
        OctopusFont caption1 = null,
        OctopusFont caption2 = null,
        OctopusFont navBarItem = null
    )
    {
        Title1 = title1;
        Title2 = title2;
        Body1 = body1;
        Body2 = body2;
        Caption1 = caption1;
        Caption2 = caption2;
        NavBarItem = navBarItem;
    }
}

public class OctopusFont
{
    public readonly string AndroidFontResourceName;
    public readonly string IOSFontName;
    public readonly float Size;

    /// <summary>Optional CSS-style weight from 100 to 900; null keeps the native default.</summary>
    public readonly int? FontWeight;

    /// <summary>Creates a font using the native default weight.</summary>
    public OctopusFont(string androidFontResourceName, string iOSFontName, float size)
        : this(androidFontResourceName, iOSFontName, size, null)
    {
    }

    /// <summary>
    /// Creates a font with an optional CSS-style weight from 100 to 900 (400 regular, 700 bold).
    /// Null keeps the native default; iOS selects the nearest named weight.
    /// Empty font names allow a weight override on the native default font.
    /// </summary>
    /// <exception cref="ArgumentOutOfRangeException">The weight is outside 100 through 900.</exception>
    public OctopusFont(string androidFontResourceName, string iOSFontName, float size, int? fontWeight)
    {
        if (fontWeight.HasValue && (fontWeight.Value < 100 || fontWeight.Value > 900))
        {
            throw new ArgumentOutOfRangeException("fontWeight", "Font weight must be between 100 and 900.");
        }
        FontWeight = fontWeight;
        AndroidFontResourceName = androidFontResourceName;
        IOSFontName = iOSFontName;
        Size = size;
    }
}
