using System.Text;
using System.Text.Json;
using LittleShop.DTOs;
using LittleShop.Models;

namespace LittleShop.Services;

public interface IRoyalMailService
{
    Task<CreateShipmentResult> CreateShipmentAsync(CreateShipmentRequest request);
    Task<TrackingResult> GetTrackingInfoAsync(string trackingNumber);
    Task<List<RoyalMailServiceOption>> GetAvailableServicesAsync(decimal weight, string country);
    Task<decimal> CalculateShippingCostAsync(decimal weight, string country, string serviceCode);
    Task<byte[]?> GenerateShippingLabelAsync(string shipmentId);
    Task<bool> CancelShipmentAsync(string shipmentId);
}

public class RoyalMailShippingService : IRoyalMailService
{
    private readonly HttpClient _httpClient;
    private readonly IConfiguration _configuration;
    private readonly ILogger<RoyalMailShippingService> _logger;
    private readonly bool _isProduction;
    private string? _accessToken;
    private DateTime _tokenExpiry;

    public RoyalMailShippingService(
        HttpClient httpClient,
        IConfiguration configuration,
        ILogger<RoyalMailShippingService> logger)
    {
        _httpClient = httpClient;
        _configuration = configuration;
        _logger = logger;
        _isProduction = !string.IsNullOrEmpty(_configuration["RoyalMail:ClientId"]);

        if (_isProduction)
        {
            _httpClient.BaseAddress = new Uri("https://api.royalmail.net/");
        }
        else
        {
            _logger.LogInformation("Royal Mail service running in development mode (API credentials not configured)");
        }
    }

    public async Task<CreateShipmentResult> CreateShipmentAsync(CreateShipmentRequest request)
    {
        if (!_isProduction)
        {
            return CreateMockShipmentResult(request);
        }

        try
        {
            await EnsureAuthenticatedAsync();

            var payload = new
            {
                shipmentType = "Delivery",
                serviceCode = request.ServiceCode ?? "1st Class",
                recipientName = request.RecipientName,
                recipientAddress = new
                {
                    addressLine1 = request.AddressLine1,
                    addressLine2 = request.AddressLine2,
                    city = request.City,
                    postCode = request.PostCode,
                    country = request.Country
                },
                senderAddress = new
                {
                    addressLine1 = _configuration["RoyalMail:SenderAddress1"] ?? "123 Business St",
                    city = _configuration["RoyalMail:SenderCity"] ?? "London",
                    postCode = _configuration["RoyalMail:SenderPostCode"] ?? "SW1A 1AA",
                    country = "United Kingdom"
                },
                weight = request.WeightInGrams,
                dimensions = new
                {
                    length = request.Length ?? 20,
                    width = request.Width ?? 15,
                    height = request.Height ?? 5
                },
                value = request.Value,
                currency = "GBP",
                reference = request.Reference
            };

            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            _httpClient.DefaultRequestHeaders.Authorization = 
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);

            var response = await _httpClient.PostAsync("shipping/v2/shipments", content);
            var responseContent = await response.Content.ReadAsStringAsync();

            if (response.IsSuccessStatusCode)
            {
                var result = JsonSerializer.Deserialize<RoyalMailShipmentResponse>(responseContent, new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                });

                return new CreateShipmentResult
                {
                    Success = true,
                    ShipmentId = result?.ShipmentId ?? Guid.NewGuid().ToString(),
                    TrackingNumber = result?.TrackingNumber ?? GenerateMockTrackingNumber(),
                    LabelUrl = result?.LabelUrl,
                    EstimatedDeliveryDate = DateTime.UtcNow.AddDays(2),
                    Cost = request.EstimatedCost ?? 5.50m
                };
            }
            else
            {
                _logger.LogError("Royal Mail API error: {StatusCode} - {Response}", 
                    response.StatusCode, responseContent);

                return new CreateShipmentResult
                {
                    Success = false,
                    ErrorMessage = $"Royal Mail API error: {response.StatusCode}"
                };
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating Royal Mail shipment");
            return new CreateShipmentResult
            {
                Success = false,
                ErrorMessage = ex.Message
            };
        }
    }

    public async Task<TrackingResult> GetTrackingInfoAsync(string trackingNumber)
    {
        if (!_isProduction)
        {
            return CreateMockTrackingResult(trackingNumber);
        }

        try
        {
            await EnsureAuthenticatedAsync();

            _httpClient.DefaultRequestHeaders.Authorization = 
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);

            var response = await _httpClient.GetAsync($"tracking/v2/items/{trackingNumber}");
            var responseContent = await response.Content.ReadAsStringAsync();

            if (response.IsSuccessStatusCode)
            {
                var result = JsonSerializer.Deserialize<RoyalMailTrackingResponse>(responseContent, new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                });

                return new TrackingResult
                {
                    Success = true,
                    TrackingNumber = trackingNumber,
                    Status = result?.Status ?? "In Transit",
                    LastUpdate = result?.LastUpdate ?? DateTime.UtcNow,
                    EstimatedDelivery = result?.EstimatedDelivery,
                    TrackingEvents = result?.Events?.Select(e => new TrackingEvent
                    {
                        Timestamp = e.Timestamp,
                        Location = e.Location,
                        Description = e.Description,
                        Status = e.Status
                    }).ToList() ?? new List<TrackingEvent>()
                };
            }
            else
            {
                return new TrackingResult
                {
                    Success = false,
                    TrackingNumber = trackingNumber,
                    ErrorMessage = $"Tracking not found: {response.StatusCode}"
                };
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting tracking info for {TrackingNumber}", trackingNumber);
            return new TrackingResult
            {
                Success = false,
                TrackingNumber = trackingNumber,
                ErrorMessage = ex.Message
            };
        }
    }

    public async Task<List<RoyalMailServiceOption>> GetAvailableServicesAsync(decimal weight, string country)
    {
        if (!_isProduction)
        {
            return CreateMockServices(weight, country);
        }

        try
        {
            await EnsureAuthenticatedAsync();

            var query = $"services?weight={weight}&country={country}";
            _httpClient.DefaultRequestHeaders.Authorization = 
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);

            var response = await _httpClient.GetAsync($"shipping/v2/{query}");
            var responseContent = await response.Content.ReadAsStringAsync();

            if (response.IsSuccessStatusCode)
            {
                var services = JsonSerializer.Deserialize<List<RoyalMailShippingServiceResponse>>(responseContent, new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                });

                return services?.Select(s => new RoyalMailServiceOption
                {
                    Code = s.Code,
                    Name = s.Name,
                    Description = s.Description,
                    Price = s.Price,
                    EstimatedDeliveryDays = s.EstimatedDeliveryDays,
                    MaxWeight = s.MaxWeight,
                    SupportsTracking = s.SupportsTracking
                }).ToList() ?? new List<RoyalMailServiceOption>();
            }

            return new List<RoyalMailServiceOption>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting Royal Mail services");
            return CreateMockServices(weight, country);
        }
    }

    public async Task<decimal> CalculateShippingCostAsync(decimal weight, string country, string serviceCode)
    {
        if (!_isProduction)
        {
            return CalculateMockShippingCost(weight, country, serviceCode);
        }

        var services = await GetAvailableServicesAsync(weight, country);
        var service = services.FirstOrDefault(s => s.Code == serviceCode);
        return service?.Price ?? 0m;
    }

    public async Task<byte[]?> GenerateShippingLabelAsync(string shipmentId)
    {
        if (!_isProduction)
        {
            return GenerateMockLabel(shipmentId);
        }

        try
        {
            await EnsureAuthenticatedAsync();

            _httpClient.DefaultRequestHeaders.Authorization = 
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);

            var response = await _httpClient.GetAsync($"shipping/v2/shipments/{shipmentId}/label");

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsByteArrayAsync();
            }

            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error generating shipping label for {ShipmentId}", shipmentId);
            return null;
        }
    }

    public async Task<bool> CancelShipmentAsync(string shipmentId)
    {
        if (!_isProduction)
        {
            _logger.LogInformation("Mock: Cancelled shipment {ShipmentId}", shipmentId);
            return true;
        }

        try
        {
            await EnsureAuthenticatedAsync();

            _httpClient.DefaultRequestHeaders.Authorization = 
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);

            var response = await _httpClient.DeleteAsync($"shipping/v2/shipments/{shipmentId}");
            return response.IsSuccessStatusCode;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error cancelling shipment {ShipmentId}", shipmentId);
            return false;
        }
    }

    private async Task EnsureAuthenticatedAsync()
    {
        if (!string.IsNullOrEmpty(_accessToken) && DateTime.UtcNow < _tokenExpiry)
            return;

        var clientId = _configuration["RoyalMail:ClientId"];
        var clientSecret = _configuration["RoyalMail:ClientSecret"];

        if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
        {
            throw new InvalidOperationException("Royal Mail API credentials not configured");
        }

        var authPayload = new
        {
            grant_type = "client_credentials",
            client_id = clientId,
            client_secret = clientSecret
        };

        var json = JsonSerializer.Serialize(authPayload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync("oauth2/token", content);
        var responseContent = await response.Content.ReadAsStringAsync();

        if (response.IsSuccessStatusCode)
        {
            var authResult = JsonSerializer.Deserialize<RoyalMailTokenResponse>(responseContent, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });

            _accessToken = authResult?.AccessToken;
            _tokenExpiry = DateTime.UtcNow.AddSeconds(authResult?.ExpiresIn ?? 3600);

            _logger.LogInformation("Successfully authenticated with Royal Mail API");
        }
        else
        {
            _logger.LogError("Failed to authenticate with Royal Mail API: {StatusCode} - {Response}", 
                response.StatusCode, responseContent);
            throw new InvalidOperationException("Royal Mail API authentication failed");
        }
    }

    // Mock methods for development
    private CreateShipmentResult CreateMockShipmentResult(CreateShipmentRequest request)
    {
        var trackingNumber = GenerateMockTrackingNumber();
        
        _logger.LogInformation("Mock: Created shipment for {Recipient} to {City}, {Country} - Tracking: {TrackingNumber}", 
            request.RecipientName, request.City, request.Country, trackingNumber);

        return new CreateShipmentResult
        {
            Success = true,
            ShipmentId = Guid.NewGuid().ToString(),
            TrackingNumber = trackingNumber,
            EstimatedDeliveryDate = DateTime.UtcNow.AddDays(GetMockDeliveryDays(request.Country)),
            Cost = CalculateMockShippingCost(request.WeightInGrams, request.Country, request.ServiceCode ?? "1st Class"),
            LabelUrl = $"https://mock-royal-mail/labels/{trackingNumber}.pdf"
        };
    }

    private TrackingResult CreateMockTrackingResult(string trackingNumber)
    {
        var statuses = new[] { "Collected", "In Transit", "Out for Delivery", "Delivered" };
        var randomStatus = statuses[new Random().Next(statuses.Length)];

        return new TrackingResult
        {
            Success = true,
            TrackingNumber = trackingNumber,
            Status = randomStatus,
            LastUpdate = DateTime.UtcNow.AddHours(-2),
            EstimatedDelivery = DateTime.UtcNow.AddDays(1),
            TrackingEvents = new List<TrackingEvent>
            {
                new() { Timestamp = DateTime.UtcNow.AddDays(-1), Location = "London", Description = "Item collected", Status = "Collected" },
                new() { Timestamp = DateTime.UtcNow.AddHours(-6), Location = "Sorting Office", Description = "Item processed", Status = "In Transit" },
                new() { Timestamp = DateTime.UtcNow.AddHours(-2), Location = "Local Depot", Description = "Item arriving at delivery office", Status = randomStatus }
            }
        };
    }

    private List<RoyalMailServiceOption> CreateMockServices(decimal weight, string country)
    {
        var services = new List<RoyalMailServiceOption>();

        if (country.Equals("United Kingdom", StringComparison.OrdinalIgnoreCase))
        {
            services.Add(new RoyalMailServiceOption
            {
                Code = "1st Class",
                Name = "First Class",
                Description = "Next working day delivery",
                Price = CalculateMockShippingCost(weight, country, "1st Class"),
                EstimatedDeliveryDays = 1,
                MaxWeight = 2000,
                SupportsTracking = true
            });

            services.Add(new RoyalMailServiceOption
            {
                Code = "2nd Class", 
                Name = "Second Class",
                Description = "2-3 working days delivery",
                Price = CalculateMockShippingCost(weight, country, "2nd Class"),
                EstimatedDeliveryDays = 3,
                MaxWeight = 2000,
                SupportsTracking = false
            });

            services.Add(new RoyalMailServiceOption
            {
                Code = "Signed For",
                Name = "Signed For 1st Class",
                Description = "Next working day with signature",
                Price = CalculateMockShippingCost(weight, country, "Signed For"),
                EstimatedDeliveryDays = 1,
                MaxWeight = 2000,
                SupportsTracking = true
            });
        }
        else
        {
            services.Add(new RoyalMailServiceOption
            {
                Code = "International Standard",
                Name = "International Standard",
                Description = "5-7 working days to Europe",
                Price = CalculateMockShippingCost(weight, country, "International Standard"),
                EstimatedDeliveryDays = 7,
                MaxWeight = 2000,
                SupportsTracking = true
            });

            services.Add(new RoyalMailServiceOption
            {
                Code = "International Tracked",
                Name = "International Tracked & Signed",
                Description = "3-5 working days with tracking",
                Price = CalculateMockShippingCost(weight, country, "International Tracked"),
                EstimatedDeliveryDays = 5,
                MaxWeight = 2000,
                SupportsTracking = true
            });
        }

        return services;
    }

    private decimal CalculateMockShippingCost(decimal weight, string country, string serviceCode)
    {
        var baseRate = country.Equals("United Kingdom", StringComparison.OrdinalIgnoreCase) ? 2.50m : 8.50m;
        var weightRate = weight / 100 * 0.50m; // £0.50 per 100g

        var serviceMultiplier = serviceCode switch
        {
            "1st Class" => 1.2m,
            "2nd Class" => 1.0m,
            "Signed For" => 1.5m,
            "International Standard" => 2.0m,
            "International Tracked" => 3.0m,
            _ => 1.0m
        };

        return Math.Round(baseRate + weightRate * serviceMultiplier, 2);
    }

    private int GetMockDeliveryDays(string country)
    {
        return country.Equals("United Kingdom", StringComparison.OrdinalIgnoreCase) ? 2 : 7;
    }

    private string GenerateMockTrackingNumber()
    {
        var random = new Random();
        return $"RM{random.Next(100000000, 999999999)}GB";
    }

    private byte[] GenerateMockLabel(string shipmentId)
    {
        // Generate a simple mock PDF label (in production this would be the actual Royal Mail label)
        var mockPdfContent = $"%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /Parent 2 0 R /Contents 4 0 R >>\nendobj\n4 0 obj\n<< /Length 44 >>\nstream\nBT\n/F1 12 Tf\n100 700 Td\n(Royal Mail Label - {shipmentId}) Tj\nET\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \n0000000174 00000 n \ntrailer\n<< /Size 5 /Root 1 0 R >>\nstartxref\n267\n%%EOF";
        return Encoding.UTF8.GetBytes(mockPdfContent);
    }
}

// DTOs and response models
public class CreateShipmentRequest
{
    public string RecipientName { get; set; } = string.Empty;
    public string AddressLine1 { get; set; } = string.Empty;
    public string? AddressLine2 { get; set; }
    public string City { get; set; } = string.Empty;
    public string PostCode { get; set; } = string.Empty;
    public string Country { get; set; } = "United Kingdom";
    public decimal WeightInGrams { get; set; }
    public decimal? Length { get; set; }
    public decimal? Width { get; set; }
    public decimal? Height { get; set; }
    public decimal Value { get; set; }
    public string? ServiceCode { get; set; }
    public string? Reference { get; set; }
    public decimal? EstimatedCost { get; set; }
}

public class CreateShipmentResult
{
    public bool Success { get; set; }
    public string? ShipmentId { get; set; }
    public string? TrackingNumber { get; set; }
    public string? LabelUrl { get; set; }
    public DateTime? EstimatedDeliveryDate { get; set; }
    public decimal Cost { get; set; }
    public string? ErrorMessage { get; set; }
}

public class TrackingResult
{
    public bool Success { get; set; }
    public string TrackingNumber { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
    public DateTime LastUpdate { get; set; }
    public DateTime? EstimatedDelivery { get; set; }
    public List<TrackingEvent> TrackingEvents { get; set; } = new();
    public string? ErrorMessage { get; set; }
}

public class TrackingEvent
{
    public DateTime Timestamp { get; set; }
    public string Location { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
}

public class RoyalMailServiceOption
{
    public string Code { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public int EstimatedDeliveryDays { get; set; }
    public decimal MaxWeight { get; set; }
    public bool SupportsTracking { get; set; }
}

// API Response models
public class RoyalMailTokenResponse
{
    public string AccessToken { get; set; } = string.Empty;
    public int ExpiresIn { get; set; }
    public string TokenType { get; set; } = string.Empty;
}

public class RoyalMailShipmentResponse
{
    public string ShipmentId { get; set; } = string.Empty;
    public string TrackingNumber { get; set; } = string.Empty;
    public string? LabelUrl { get; set; }
    public DateTime EstimatedDelivery { get; set; }
}

public class RoyalMailTrackingResponse
{
    public string Status { get; set; } = string.Empty;
    public DateTime LastUpdate { get; set; }
    public DateTime? EstimatedDelivery { get; set; }
    public List<RoyalMailTrackingEvent> Events { get; set; } = new();
}

public class RoyalMailTrackingEvent
{
    public DateTime Timestamp { get; set; }
    public string Location { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
}

public class RoyalMailShippingServiceResponse
{
    public string Code { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public int EstimatedDeliveryDays { get; set; }
    public decimal MaxWeight { get; set; }
    public bool SupportsTracking { get; set; }
}