using System.Net;
using MaxMind.GeoIP2;

namespace TrackQrApi.Features.Events.Services;

public interface IGeoIpService
{
    string? GetCountryCode(string ipAddress);
}

public class GeoIpService : IGeoIpService, IDisposable
{
    private readonly ILogger<GeoIpService> _logger;
    private readonly DatabaseReader? _reader;

    public GeoIpService(IConfiguration configuration, ILogger<GeoIpService> logger)
    {
        _logger = logger;
        var dbPath = configuration["GeoIP:DatabasePath"];

        if (!string.IsNullOrEmpty(dbPath) && File.Exists(dbPath))
            try
            {
                _reader = new DatabaseReader(dbPath);
                _logger.LogInformation("GeoIP database loaded from {Path}", dbPath);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to load GeoIP database from {Path}", dbPath);
            }
        else
            _logger.LogInformation("GeoIP database not configured or not found. Country detection disabled.");
    }

    public void Dispose()
    {
        _reader?.Dispose();
    }

    public string? GetCountryCode(string ipAddress)
    {
        if (_reader == null)
            return null;

        try
        {
            // Handle localhost and private IPs
            if (ipAddress == "127.0.0.1" || ipAddress == "::1" || IsPrivateIp(ipAddress)) return null;

            if (!IPAddress.TryParse(ipAddress, out var ip)) return null;

            if (_reader.TryCountry(ip, out var response)) return response?.Country?.IsoCode;
        }
        catch (Exception ex)
        {
            _logger.LogDebug(ex, "Failed to lookup country for IP {IP}", ipAddress);
        }

        return null;
    }

    private static bool IsPrivateIp(string ipAddress)
    {
        if (!IPAddress.TryParse(ipAddress, out var ip))
            return false;

        var bytes = ip.GetAddressBytes();

        // Check for IPv4 private ranges
        if (bytes.Length == 4)
        {
            // 10.0.0.0/8
            if (bytes[0] == 10) return true;
            // 172.16.0.0/12
            if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true;
            // 192.168.0.0/16
            if (bytes[0] == 192 && bytes[1] == 168) return true;
        }

        return false;
    }
}