using Microsoft.EntityFrameworkCore;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using LittleShop.Data;
using LittleShop.Models;
using LittleShop.DTOs;
using LittleShop.Enums;

namespace LittleShop.Services;

public class CryptoPaymentService : ICryptoPaymentService
{
    private readonly LittleShopContext _context;
    private readonly IBTCPayServerService _btcPayService;
    private readonly ILogger<CryptoPaymentService> _logger;

    public CryptoPaymentService(
        LittleShopContext context, 
        IBTCPayServerService btcPayService,
        ILogger<CryptoPaymentService> logger)
    {
        _context = context;
        _btcPayService = btcPayService;
        _logger = logger;
    }

    public async Task<CryptoPaymentDto> CreatePaymentAsync(Guid orderId, CryptoCurrency currency)
    {
        var order = await _context.Orders
            .Include(o => o.Items)
            .ThenInclude(oi => oi.Product)
            .FirstOrDefaultAsync(o => o.Id == orderId);

        if (order == null)
            throw new ArgumentException("Order not found", nameof(orderId));

        // Check if payment already exists for this currency
        var existingPayment = await _context.CryptoPayments
            .FirstOrDefaultAsync(cp => cp.OrderId == orderId && cp.Currency == currency && cp.Status != PaymentStatus.Expired);

        if (existingPayment != null)
        {
            return MapToDto(existingPayment);
        }

        // Create BTCPay Server invoice
        var invoiceId = await _btcPayService.CreateInvoiceAsync(
            order.TotalAmount, 
            currency, 
            order.Id.ToString(),
            $"Order #{order.Id} - {order.Items.Count} items"
        );

        // For now, generate a placeholder wallet address
        // In a real implementation, this would come from BTCPay Server
        var walletAddress = GenerateWalletAddress(currency);

        var cryptoPayment = new CryptoPayment
        {
            Id = Guid.NewGuid(),
            OrderId = orderId,
            Currency = currency,
            WalletAddress = walletAddress,
            RequiredAmount = order.TotalAmount, // This should be converted to crypto amount
            PaidAmount = 0,
            Status = PaymentStatus.Pending,
            BTCPayInvoiceId = invoiceId, // This is the actual BTCPay invoice ID
            CreatedAt = DateTime.UtcNow,
            ExpiresAt = DateTime.UtcNow.AddHours(24)
        };

        _context.CryptoPayments.Add(cryptoPayment);
        await _context.SaveChangesAsync();

        _logger.LogInformation("Created crypto payment {PaymentId} for order {OrderId} with currency {Currency}", 
            cryptoPayment.Id, orderId, currency);

        return MapToDto(cryptoPayment);
    }

    public async Task<IEnumerable<CryptoPaymentDto>> GetPaymentsByOrderAsync(Guid orderId)
    {
        var payments = await _context.CryptoPayments
            .Where(cp => cp.OrderId == orderId)
            .OrderByDescending(cp => cp.CreatedAt)
            .ToListAsync();

        return payments.Select(MapToDto);
    }

    public async Task<PaymentStatusDto> GetPaymentStatusAsync(Guid paymentId)
    {
        var payment = await _context.CryptoPayments.FindAsync(paymentId);
        if (payment == null)
            throw new ArgumentException("Payment not found", nameof(paymentId));

        return new PaymentStatusDto
        {
            PaymentId = payment.Id,
            Status = payment.Status,
            RequiredAmount = payment.RequiredAmount,
            PaidAmount = payment.PaidAmount,
            PaidAt = payment.PaidAt,
            ExpiresAt = payment.ExpiresAt
        };
    }

    public async Task<bool> ProcessPaymentWebhookAsync(string invoiceId, PaymentStatus status, decimal amount, string? transactionHash = null)
    {
        var payment = await _context.CryptoPayments
            .FirstOrDefaultAsync(cp => cp.BTCPayInvoiceId == invoiceId);

        if (payment == null)
        {
            _logger.LogWarning("Received webhook for unknown invoice {InvoiceId}", invoiceId);
            return false;
        }

        payment.Status = status;
        payment.PaidAmount = amount;
        payment.TransactionHash = transactionHash;

        if (status == PaymentStatus.Paid)
        {
            payment.PaidAt = DateTime.UtcNow;
            
            // Update order status
            var order = await _context.Orders.FindAsync(payment.OrderId);
            if (order != null)
            {
                order.Status = OrderStatus.PaymentReceived;
                order.PaidAt = DateTime.UtcNow;
            }
        }

        await _context.SaveChangesAsync();

        _logger.LogInformation("Processed payment webhook for invoice {InvoiceId}, status: {Status}", 
            invoiceId, status);

        return true;
    }

    private static CryptoPaymentDto MapToDto(CryptoPayment payment)
    {
        return new CryptoPaymentDto
        {
            Id = payment.Id,
            OrderId = payment.OrderId,
            Currency = payment.Currency,
            WalletAddress = payment.WalletAddress,
            RequiredAmount = payment.RequiredAmount,
            PaidAmount = payment.PaidAmount,
            Status = payment.Status,
            BTCPayInvoiceId = payment.BTCPayInvoiceId,
            TransactionHash = payment.TransactionHash,
            CreatedAt = payment.CreatedAt,
            PaidAt = payment.PaidAt,
            ExpiresAt = payment.ExpiresAt
        };
    }

    private static string GenerateWalletAddress(CryptoCurrency currency)
    {
        // Placeholder wallet addresses - in production these would come from BTCPay Server
        var guid = Guid.NewGuid().ToString("N"); // 32 characters
        return currency switch
        {
            CryptoCurrency.BTC => "bc1q" + guid[..26],
            CryptoCurrency.XMR => "4" + guid + guid[..32], // XMR needs ~95 chars, use double GUID
            CryptoCurrency.USDT => "0x" + guid[..32], // ERC-20 address
            CryptoCurrency.LTC => "ltc1q" + guid[..26],
            CryptoCurrency.ETH => "0x" + guid[..32],
            CryptoCurrency.ZEC => "zs1" + guid + guid[..29], // Shielded address
            CryptoCurrency.DASH => "X" + guid[..30],
            CryptoCurrency.DOGE => "D" + guid[..30],
            _ => "placeholder_" + guid[..20]
        };
    }
}