using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Tiku.Api.Contracts;
using Tiku.Api.Security;
using Tiku.Application.Commerce;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
using Tiku.Domain.Commerce;

namespace Tiku.Api.Controllers;

[ApiController]
[Tags("学生端-交易")]
[Authorize(Policy = TikuPolicies.CurrentTenantMember)]
[RequireSaasFeature(SaasFeatureCatalog.StudentStore)]
[Produces("application/json")]
[Route("api/student/commerce")]
public sealed class CommerceController(
    ICommerceService commerceService,
    ICommerceAdminService commerceAdminService,
    ICurrentUser currentUser,
    ITenantContext currentTenant,
    ITenantContextInitializer tenantInitializer,
    ITenantDirectory tenantDirectory) : ControllerBase
{
    [HttpPost("orders")]
    [EndpointSummary("创建学生端订单")]
    [ProducesResponseType<CommerceOrderItem>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceOrderItem>> CreateOrder(
        CreateCommerceOrderDto request,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.CreateOrderAsync(
            ResolveActor(),
            request.ToCommand(),
            cancellationToken));
    }

    [HttpGet("orders")]
    [EndpointSummary("查询当前用户订单")]
    [ProducesResponseType<CommerceOrderList>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceOrderList>> Orders(
        [FromQuery] CommerceOrderQueryDto query,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.GetOrdersAsync(
            ResolveActor(),
            new CommerceOrderQuery(query.Limit, query.Status),
            cancellationToken));
    }

    [HttpGet("orders/{orderNo}")]
    [EndpointSummary("查询当前用户订单详情")]
    [ProducesResponseType<CommerceOrderItem>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceOrderItem>> Order(
        string orderNo,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.GetOrderAsync(
            ResolveActor(),
            orderNo,
            cancellationToken));
    }

    [HttpPost("payments")]
    [EndpointSummary("创建订单支付")]
    [ProducesResponseType<CommercePaymentItem>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommercePaymentItem>> CreatePayment(
        CreateCommercePaymentDto request,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.CreatePaymentAsync(
            ResolveActor(),
            request.ToCommand(),
            cancellationToken));
    }

    [HttpGet("entitlements/current")]
    [EndpointSummary("查询当前用户权益")]
    [ProducesResponseType<CurrentEntitlementItem>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CurrentEntitlementItem>> CurrentEntitlement(
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.GetCurrentEntitlementAsync(
            ResolveActor(),
            cancellationToken));
    }

    [HttpPost("coupons/claim")]
    [EndpointSummary("领取优惠券")]
    [ProducesResponseType<CommerceCouponItem>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceCouponItem>> ClaimCoupon(
        ClaimCommerceCouponDto request,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.ClaimCouponAsync(
            ResolveActor(),
            request.ToCommand(),
            cancellationToken));
    }

    [HttpGet("coupons")]
    [EndpointSummary("查询当前用户优惠券")]
    [ProducesResponseType<CommerceCouponList>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceCouponList>> Coupons(
        [FromQuery] CommerceCouponQueryDto query,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.GetCouponsAsync(
            ResolveActor(),
            query.ToQuery(),
            cancellationToken));
    }

    [HttpPost("coupons/check")]
    [EndpointSummary("校验优惠券并预览订单金额")]
    [ProducesResponseType<CommerceCouponCheckResult>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceCouponCheckResult>> CheckCoupon(
        CheckCommerceCouponDto request,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceService.CheckCouponAsync(
            ResolveActor(),
            request.ToCommand(),
            cancellationToken));
    }

    [AllowAnonymous]
    [HttpPost("payments/notify/wechat-pay")]
    [EndpointSummary("微信支付回调")]
    [ProducesResponseType<PaymentNotificationProcessResult>(StatusCodes.Status200OK)]
    public async Task<ActionResult<PaymentNotificationProcessResult>> WechatPayNotify(
        [FromQuery] string? tenantCode,
        CancellationToken cancellationToken)
    {
        return Ok(await ProcessNotificationAsync(
            await ResolveNotificationTenantAsync(tenantCode, cancellationToken),
            PaymentProviders.WechatPay,
            cancellationToken));
    }

    [AllowAnonymous]
    [HttpPost("payments/notify/alipay")]
    [EndpointSummary("支付宝支付回调")]
    [ProducesResponseType<PaymentNotificationProcessResult>(StatusCodes.Status200OK)]
    public async Task<ActionResult<PaymentNotificationProcessResult>> AlipayNotify(
        [FromQuery] string? tenantCode,
        CancellationToken cancellationToken)
    {
        return Ok(await ProcessNotificationAsync(
            await ResolveNotificationTenantAsync(tenantCode, cancellationToken),
            PaymentProviders.Alipay,
            cancellationToken));
    }

    [AllowAnonymous]
    [HttpPost("refunds/notify/wechat_pay")]
    [EndpointSummary("微信退款结果回调")]
    [ProducesResponseType<CommerceRefundRequest>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceRefundRequest>> WechatRefundNotify(
        [FromQuery] string? tenantCode,
        RefundNotificationDto request,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceAdminService.ProcessRefundNotificationAsync(
            await ResolveNotificationTenantAsync(tenantCode, cancellationToken),
            request.ToCommand(PaymentProviders.WechatPay),
            cancellationToken));
    }

    [AllowAnonymous]
    [HttpPost("refunds/notify/alipay")]
    [EndpointSummary("支付宝退款结果回调")]
    [ProducesResponseType<CommerceRefundRequest>(StatusCodes.Status200OK)]
    public async Task<ActionResult<CommerceRefundRequest>> AlipayRefundNotify(
        [FromQuery] string? tenantCode,
        RefundNotificationDto request,
        CancellationToken cancellationToken)
    {
        return Ok(await commerceAdminService.ProcessRefundNotificationAsync(
            await ResolveNotificationTenantAsync(tenantCode, cancellationToken),
            request.ToCommand(PaymentProviders.Alipay),
            cancellationToken));
    }

    private async Task<Guid> ResolveNotificationTenantAsync(
        string? tenantCode,
        CancellationToken cancellationToken)
    {
        if (currentTenant.TenantId.HasValue) return currentTenant.TenantId.Value;

        if (string.IsNullOrWhiteSpace(tenantCode))
            throw new CommerceException("Tenant code is required for payment notification.", "tenant_required");

        var tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), cancellationToken)
                     ?? throw new CommerceException("Tenant was not found.", "tenant_not_found");
        tenantInitializer.Initialize(tenant.TenantId, tenant.TenantCode, TenantResolutionSource.TenantCode);
        return tenant.TenantId;
    }

    private CommerceActor ResolveActor()
    {
        if (currentTenant.TenantId is null || currentUser.UserId is null)
            throw new CommerceException("Current commerce actor was not resolved.", "commerce_access_denied");

        return new CommerceActor(currentTenant.TenantId.Value, currentUser.UserId.Value);
    }

    private async Task<PaymentNotificationProcessResult> ProcessNotificationAsync(
        Guid tenantId,
        string provider,
        CancellationToken cancellationToken)
    {
        if (tenantId == Guid.Empty)
            throw new CommerceException("Tenant id is required for payment notification.", "tenant_required");

        var rawBody = await ReadRawBodyAsync(Request, cancellationToken);
        using var body = ParseNotificationBody(Request, rawBody, cancellationToken);
        var headers = Request.Headers.ToDictionary(
            pair => pair.Key,
            pair => pair.Value.ToString(),
            StringComparer.OrdinalIgnoreCase);

        return await commerceService.ProcessPaymentNotificationAsync(
            tenantId,
            provider,
            headers,
            rawBody,
            body.RootElement.Clone(),
            cancellationToken);
    }

    private static async Task<string> ReadRawBodyAsync(HttpRequest request, CancellationToken cancellationToken)
    {
        using var reader = new StreamReader(
            request.Body,
            Encoding.UTF8,
            false,
            leaveOpen: false);
        return await reader.ReadToEndAsync(cancellationToken);
    }

    private static JsonDocument ParseNotificationBody(
        HttpRequest request,
        string rawBody,
        CancellationToken cancellationToken)
    {
        if (request.HasFormContentType)
        {
            _ = cancellationToken;
            var form = QueryHelpers.ParseQuery(rawBody);
            var values = form.ToDictionary(
                pair => pair.Key,
                pair => pair.Value.ToString(),
                StringComparer.Ordinal);
            return JsonDocument.Parse(JsonSerializer.Serialize(values));
        }

        if (string.IsNullOrWhiteSpace(rawBody)) return JsonDocument.Parse("{}");

        return JsonDocument.Parse(rawBody);
    }
}