using System.Net.Http.Json;
using Meezi.Core.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Meezi.Infrastructure.ExternalServices;

/// <summary>
/// Pushe (pushe.co) push-notification gateway — Iran-safe alternative to FCM.
///
/// Config is read from the platform runtime config (DB) first, then falls back
/// to IConfiguration:
///   integrations.pushe.token   / "Pushe:Token"   — Pushe REST API token
///   integrations.pushe.appId   / "Pushe:AppId"   — Pushe application id (app_ids)
///   integrations.pushe.enabled / "Pushe:Enabled"
///
/// Send endpoint: POST {BaseUrl}/messaging/notifications/
/// Verify the exact request body against the current Pushe API docs — the
/// shape below targets the v2 messaging API (topic via "data.subscriptions",
/// devices via "devices").
/// </summary>
public class PusheNotificationSender : IPushSender
{
    private const string DbKeyToken   = "integrations.pushe.token";
    private const string DbKeyAppId   = "integrations.pushe.appId";
    private const string DbKeyEnabled = "integrations.pushe.enabled";

    private const string BaseUrl = "https://api.pushe.co/v2";

    private readonly HttpClient _http;
    private readonly IConfiguration _configuration;
    private readonly IPlatformRuntimeConfig _platform;
    private readonly IHostEnvironment _environment;
    private readonly ILogger<PusheNotificationSender> _logger;

    public PusheNotificationSender(
        HttpClient http,
        IConfiguration configuration,
        IPlatformRuntimeConfig platform,
        IHostEnvironment environment,
        ILogger<PusheNotificationSender> logger)
    {
        _http = http;
        _configuration = configuration;
        _platform = platform;
        _environment = environment;
        _logger = logger;
    }

    public Task SendToTopicAsync(
        string topic, string title, string body, string? deepLink = null,
        CancellationToken cancellationToken = default)
        => SendAsync(new { topics = new[] { topic } }, title, body, deepLink, $"topic '{topic}'", cancellationToken);

    public Task SendToTokensAsync(
        IReadOnlyCollection<string> tokens, string title, string body, string? deepLink = null,
        CancellationToken cancellationToken = default)
    {
        if (tokens.Count == 0) return Task.CompletedTask;
        return SendAsync(new { devices = tokens.ToArray() }, title, body, deepLink,
            $"{tokens.Count} device(s)", cancellationToken);
    }

    private async Task SendAsync(
        object target, string title, string body, string? deepLink,
        string targetDescription, CancellationToken ct)
    {
        var (token, appId, enabled) = await GetConfigAsync(ct);

        if (!enabled || string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(appId))
        {
            _logger.LogInformation(
                "Pushe not configured — push to {Target} skipped: {Title}", targetDescription, title);
            return;
        }

        if (_environment.IsDevelopment())
        {
            _logger.LogWarning("[DEV PUSH] {Target} :: {Title} — {Body}", targetDescription, title, body);
            return; // Skip real push in development
        }

        var notification = new Dictionary<string, object?>
        {
            ["title"] = title,
            ["content"] = body,
        };
        if (!string.IsNullOrWhiteSpace(deepLink))
        {
            notification["action"] = new { action_type = "U", url = deepLink };
        }

        var payload = new Dictionary<string, object?>
        {
            ["app_ids"] = new[] { appId },
            ["data"] = new Dictionary<string, object?>
            {
                ["notification"] = notification,
            },
        };
        // Merge target (topics / devices) into "data".
        foreach (var prop in target.GetType().GetProperties())
        {
            ((Dictionary<string, object?>)payload["data"]!)[prop.Name] = prop.GetValue(target);
        }

        try
        {
            using var req = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/messaging/notifications/")
            {
                Content = JsonContent.Create(payload),
            };
            req.Headers.TryAddWithoutValidation("Authorization", $"Token {token}");

            var response = await _http.SendAsync(req, ct);
            if (!response.IsSuccessStatusCode)
            {
                var detail = await response.Content.ReadAsStringAsync(ct);
                _logger.LogWarning(
                    "Pushe send to {Target} failed: {Status} {Detail}",
                    targetDescription, (int)response.StatusCode, detail);
                return;
            }

            _logger.LogInformation("Pushe push sent to {Target}: {Title}", targetDescription, title);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Pushe send to {Target} threw", targetDescription);
        }
    }

    private async Task<(string? Token, string? AppId, bool Enabled)> GetConfigAsync(CancellationToken ct)
    {
        var enabledRaw = await _platform.GetAsync(DbKeyEnabled, ct)
                         ?? _configuration["Pushe:Enabled"];
        var enabled = !string.Equals(enabledRaw, "false", StringComparison.OrdinalIgnoreCase);

        var token = await _platform.GetAsync(DbKeyToken, ct)
                    ?? _configuration["Pushe:Token"];
        var appId = await _platform.GetAsync(DbKeyAppId, ct)
                    ?? _configuration["Pushe:AppId"];

        return (token, appId, enabled);
    }
}
