using System.Diagnostics;
using System.IO;
using System.Text.Json;
using Nexus.Contracts.Apps;
using NexusShell.Models;

namespace NexusShell.Services;

public enum LockdownDecision
{
    Allowed,
    Blocked,
    LockdownDisabled,
}

public sealed class LockdownService
{
    private readonly LockdownConfig _config;
    private readonly bool _safePocEnabled;
    private readonly List<LockdownAuditEntry> _auditLog = [];
    private readonly object _auditLock = new();
    private readonly string _auditFilePath;

    public LockdownService(bool safePocEnabled = true)
    {
        _safePocEnabled = safePocEnabled;
        _config = LoadConfig();
        if (!_safePocEnabled)
        {
            _config.LockdownMode = false;
            _config.AllowWindowsEscape = false;
            _config.DeveloperConsoleEnabled = false;
        }

        _auditFilePath = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "NinjaNexus",
            "audit",
            "lockdown-audit.jsonl");

        Directory.CreateDirectory(Path.GetDirectoryName(_auditFilePath)!);
        Log("lockdown.init", "system",
            $"SafePoc={_safePocEnabled}, LockdownMode={_config.LockdownMode}, allowed={_config.AllowedApps.Count}, blocked={_config.BlockedApps.Count}",
            true);
    }

    public LockdownConfig Config => _config;

    public bool IsLockdownActive => _safePocEnabled && _config.LockdownMode && !_platformBypass;

    private bool _platformBypass;

    public void SetPlatformBypass(bool bypass) => _platformBypass = bypass;

    public bool IsSafePocEnabled => _safePocEnabled;

    public int AllowedAppsCount => _config.AllowedApps.Count;

    public int BlockedAppsCount => _config.BlockedApps.Count;

    public bool DeveloperConsoleEnabled => _safePocEnabled && _config.DeveloperConsoleEnabled;

    public string AuditFilePath => _auditFilePath;

    public IReadOnlyList<LockdownAuditEntry> AuditLog
    {
        get
        {
            lock (_auditLock)
            {
                return _auditLog.ToList();
            }
        }
    }

    public (LockdownDecision Decision, string? UserMessage) EvaluateLaunch(AppEntry app)
    {
        if (!IsLockdownActive)
        {
            return (LockdownDecision.LockdownDisabled, null);
        }

        var executable = Path.GetFileName(app.ExecutablePath).ToLowerInvariant();
        var arguments = (app.Arguments ?? string.Empty).ToLowerInvariant();

        foreach (var blocked in _config.BlockedApps)
        {
            var token = blocked.ToLowerInvariant();
            if (token.EndsWith(':') && arguments.Contains(token, StringComparison.Ordinal))
            {
                LogBlocked(app, $"blocked protocol/argument: {token}");
                return (LockdownDecision.Blocked, "Denne funktion er låst af Ninja Nexus.");
            }

            var blockedExe = token.EndsWith(".exe", StringComparison.Ordinal) ? token : $"{token}.exe";
            if (string.Equals(executable, blockedExe, StringComparison.Ordinal) ||
                string.Equals(executable, token, StringComparison.Ordinal))
            {
                LogBlocked(app, $"blocked executable: {executable}");
                return (LockdownDecision.Blocked, GetBlockedLaunchMessage(executable));
            }
        }

        if (IsAllowed(app))
        {
            Log("app.launch.allowed", "user", $"{app.Name} ({executable})", true);
            return (LockdownDecision.Allowed, null);
        }

        LogBlocked(app, "not in allowedApps list");
        return (LockdownDecision.Blocked, "Denne funktion er låst af Ninja Nexus.");
    }

    public void LogDeveloperAttempt(string actor, string detail, bool success)
    {
        Log(success ? "developer.console.auth.success" : "developer.console.auth.failed", actor, detail, success);
    }

    public void LogDeveloperAction(string actor, string action, bool success)
    {
        Log("developer.console.action", actor, action, success);
    }

    private bool IsAllowed(AppEntry app)
    {
        var executable = Path.GetFileNameWithoutExtension(app.ExecutablePath).ToLowerInvariant();
        var appId = app.Id.ToLowerInvariant();
        var appName = app.Name.ToLowerInvariant();

        foreach (var allowed in _config.AllowedApps)
        {
            var token = allowed.ToLowerInvariant();
            if (appId.Contains(token, StringComparison.Ordinal) ||
                executable.Contains(token, StringComparison.Ordinal) ||
                appName.Contains(token, StringComparison.Ordinal))
            {
                return true;
            }
        }

        return false;
    }

    private static string GetBlockedLaunchMessage(string executable)
    {
        var lower = executable.ToLowerInvariant();
        if (string.Equals(lower, "calc.exe", StringComparison.Ordinal))
        {
            return "Åbn Nexus Calculator i stedet.";
        }

        if (lower.Contains("photos", StringComparison.Ordinal))
        {
            return "Åbn Nexus Photos i stedet.";
        }

        if (lower is "wmp.exe" or "wmplayer.exe" ||
            lower.Contains("zunevideo", StringComparison.Ordinal) ||
            lower.Contains("zunemusic", StringComparison.Ordinal))
        {
            return "Åbn Nexus Media i stedet.";
        }

        return "Denne funktion er låst af Ninja Nexus.";
    }

    private void LogBlocked(AppEntry app, string reason)
    {
        var detail = $"{app.Name} — {reason}";
        Log("app.launch.blocked", "user", detail, false);
        Debug.WriteLine($"[Nexus Lockdown] BLOCKED: {detail}");
    }

    private void Log(string eventType, string actor, string detail, bool success)
    {
        var entry = new LockdownAuditEntry
        {
            Timestamp = DateTimeOffset.UtcNow,
            EventType = eventType,
            Actor = actor,
            Detail = detail,
            Success = success,
        };

        lock (_auditLock)
        {
            _auditLog.Add(entry);
            if (_auditLog.Count > 200)
            {
                _auditLog.RemoveAt(0);
            }
        }

        PersistAuditEntry(entry);
        Debug.WriteLine($"[Nexus Lockdown] {entry.Timestamp:O} {eventType} success={success} {detail}");
    }

    private void PersistAuditEntry(LockdownAuditEntry entry)
    {
        try
        {
            var line = JsonSerializer.Serialize(entry, AuditJsonOptions);
            File.AppendAllText(_auditFilePath, line + Environment.NewLine);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[Nexus Lockdown] audit persist failed: {ex.Message}");
        }
    }

    private static LockdownConfig LoadConfig()
    {
        var path = Path.Combine(AppContext.BaseDirectory, "config", "lockdown.poc.json");
        if (!File.Exists(path))
        {
            Debug.WriteLine("[Nexus Lockdown] config not found — lockdown disabled");
            return new LockdownConfig { LockdownMode = false };
        }

        var json = File.ReadAllText(path);
        return JsonSerializer.Deserialize<LockdownConfig>(json, JsonOptions) ?? new LockdownConfig();
    }

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

    private static readonly JsonSerializerOptions AuditJsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    };
}
