using RackPeek.Domain.Resources.AccessPoints;
using RackPeek.Domain.Resources.Desktops;
using RackPeek.Domain.Resources.Firewalls;
using RackPeek.Domain.Resources.Laptops;
using RackPeek.Domain.Resources.Routers;
using RackPeek.Domain.Resources.Servers;
using RackPeek.Domain.Resources.Services;
using RackPeek.Domain.Resources.Switches;
using RackPeek.Domain.Resources.SystemResources;
using RackPeek.Domain.Resources.UpsUnits;

namespace RackPeek.Domain.Resources;

public abstract class Resource {
    private static readonly string[] _hardwareTypes =
        ["server", "switch", "firewall", "router", "accesspoint", "desktop", "laptop", "ups"];

    private static readonly Dictionary<string, string> _kindToPluralDictionary = new()
    {
        { "hardware", "hardware" },
        { "server", "servers" },
        { "switch", "switches" },
        { "firewall", "firewalls" },
        { "router", "routers" },
        { "accesspoint", "accesspoints" },
        { "desktop", "desktops" },
        { "laptop", "laptops" },
        { "ups", "ups" },
        { "system", "systems" },
        { "service", "services" }
    };

    private static readonly Dictionary<Type, string> _typeToKindMap = new()
    {
        { typeof(Hardware.Hardware), "Hardware" },
        { typeof(Server), "Server" },
        { typeof(Switch), "Switch" },
        { typeof(Firewall), "Firewall" },
        { typeof(Router), "Router" },
        { typeof(AccessPoint), "Accesspoint" },
        { typeof(Desktop), "Desktop" },
        { typeof(Laptop), "Laptop" },
        { typeof(Ups), "Ups" },
        { typeof(SystemResource), "System" },
        { typeof(Service), "Service" }
    };

    public string Kind { get; set; } = string.Empty;

    public required string Name { get; set; }

    public string[] Tags { get; set; } = [];
    public Dictionary<string, string> Labels { get; set; } = new();
    public string? Notes { get; set; }

    public List<string> RunsOn { get; set; } = new();

    public static bool IsHardware(string kind) {
        kind = kind.Trim().ToLower();
        return kind == "hardware" || _hardwareTypes.Contains(kind);
    }

    public static string GetResourceUrl(string kind, string name) {
        var encoded = Uri.EscapeDataString(name);

        kind = kind.Trim().ToLower();
        if (IsHardware(kind)) return $"resources/hardware/{encoded}";

        if (kind == "system") return $"resources/systems/{encoded}";

        if (kind == "service") return $"resources/services/{encoded}";

        return "#";
    }

    public static string KindToPlural(string kind) =>
        _kindToPluralDictionary.GetValueOrDefault(kind.ToLower().Trim(), kind);

    public static string GetKind<T>() where T : Resource {
        if (_typeToKindMap.TryGetValue(typeof(T), out var kind))
            return kind;

        throw new InvalidOperationException(
            $"No kind mapping defined for type {typeof(T).Name}");
    }

    public static bool CanRunOn<T>(Resource parent) where T : Resource {
        var childKind = GetKind<T>().ToLowerInvariant();
        var parentKind = parent.Kind.ToLowerInvariant();

        // Service -> System
        if (childKind == "service" && parentKind == "system")
            return true;

        // System -> Hardware
        if (childKind == "system" && parent is Hardware.Hardware)
            return true;

        // System -> System
        if (childKind == "system" && parent is SystemResource)
            return true;

        return false;
    }
}
