using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TakeoutSaaS.Application.App.Finance.Settlement.Dto;
using TakeoutSaaS.Application.App.Finance.Settlement.Queries;
using TakeoutSaaS.Application.App.Stores.Services;
using TakeoutSaaS.Domain.Payments.Enums;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Module.Authorization.Attributes;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Web.Api;
using TakeoutSaaS.TenantApi.Contracts.Finance;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 财务中心到账查询。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/finance/settlement")]
public sealed class FinanceSettlementController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    private const string ViewPermission = "tenant:finance:settlement:view";
    private const string ExportPermission = "tenant:finance:settlement:export";

    /// <summary>
    /// 查询到账统计。
    /// </summary>
    [HttpGet("stats")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceSettlementStatsResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceSettlementStatsResponse>> Stats(
        [FromQuery] FinanceSettlementStatsRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var stats = await mediator.Send(new GetFinanceSettlementStatsQuery
        {
            StoreId = storeId
        }, cancellationToken);

        return ApiResponse<FinanceSettlementStatsResponse>.Ok(new FinanceSettlementStatsResponse
        {
            TodayArrivedAmount = stats.TodayArrivedAmount,
            YesterdayArrivedAmount = stats.YesterdayArrivedAmount,
            CurrentMonthArrivedAmount = stats.CurrentMonthArrivedAmount,
            CurrentMonthTransactionCount = stats.CurrentMonthTransactionCount
        });
    }

    /// <summary>
    /// 查询到账账户信息。
    /// </summary>
    [HttpGet("account")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceSettlementAccountResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceSettlementAccountResponse>> Account(
        CancellationToken cancellationToken)
    {
        var account = await mediator.Send(new GetFinanceSettlementAccountQuery(), cancellationToken);
        if (account is null)
        {
            return ApiResponse<FinanceSettlementAccountResponse>.Error(ErrorCodes.NotFound, "结算账户信息不存在");
        }

        return ApiResponse<FinanceSettlementAccountResponse>.Ok(new FinanceSettlementAccountResponse
        {
            BankName = account.BankName,
            BankAccountName = account.BankAccountName,
            BankAccountNoMasked = account.BankAccountNoMasked,
            WechatMerchantNoMasked = account.WechatMerchantNoMasked,
            AlipayPidMasked = account.AlipayPidMasked,
            SettlementPeriodText = account.SettlementPeriodText
        });
    }

    /// <summary>
    /// 查询到账汇总列表。
    /// </summary>
    [HttpGet("list")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceSettlementListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceSettlementListResultResponse>> List(
        [FromQuery] FinanceSettlementListRequest request,
        CancellationToken cancellationToken)
    {
        var parsed = await ParseFilterAsync(request, cancellationToken);

        var result = await mediator.Send(new SearchFinanceSettlementListQuery
        {
            StoreId = parsed.StoreId,
            StartAt = parsed.StartAt,
            EndAt = parsed.EndAt,
            PaymentMethod = parsed.PaymentMethod,
            Page = Math.Max(1, request.Page),
            PageSize = Math.Clamp(request.PageSize, 1, 200)
        }, cancellationToken);

        return ApiResponse<FinanceSettlementListResultResponse>.Ok(new FinanceSettlementListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.Total,
            Page = result.Page,
            PageSize = result.PageSize
        });
    }

    /// <summary>
    /// 查询到账明细（展开行）。
    /// </summary>
    [HttpGet("detail")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceSettlementDetailResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceSettlementDetailResultResponse>> Detail(
        [FromQuery] FinanceSettlementDetailRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var arrivedDate = ParseRequiredDate(request.ArrivedDate, nameof(request.ArrivedDate));
        var paymentMethod = ParseRequiredSettlementChannel(request.Channel);

        var result = await mediator.Send(new GetFinanceSettlementDetailQuery
        {
            StoreId = storeId,
            ArrivedDate = arrivedDate,
            PaymentMethod = paymentMethod,
            Take = 50
        }, cancellationToken);

        return ApiResponse<FinanceSettlementDetailResultResponse>.Ok(new FinanceSettlementDetailResultResponse
        {
            Items = result.Items.Select(MapDetailItem).ToList()
        });
    }

    /// <summary>
    /// 导出到账汇总 CSV。
    /// </summary>
    [HttpGet("export")]
    [PermissionAuthorize(ExportPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceSettlementExportResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceSettlementExportResponse>> Export(
        [FromQuery] FinanceSettlementFilterRequest request,
        CancellationToken cancellationToken)
    {
        var parsed = await ParseFilterAsync(request, cancellationToken);

        var result = await mediator.Send(new ExportFinanceSettlementCsvQuery
        {
            StoreId = parsed.StoreId,
            StartAt = parsed.StartAt,
            EndAt = parsed.EndAt,
            PaymentMethod = parsed.PaymentMethod
        }, cancellationToken);

        return ApiResponse<FinanceSettlementExportResponse>.Ok(new FinanceSettlementExportResponse
        {
            FileName = result.FileName,
            FileContentBase64 = result.FileContentBase64,
            TotalCount = result.TotalCount
        });
    }

    private async Task<(long StoreId, DateTime? StartAt, DateTime? EndAt, PaymentMethod? PaymentMethod)> ParseFilterAsync(
        FinanceSettlementFilterRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var startAt = ParseDateOrNull(request.StartDate);
        var endAt = ParseDateOrNull(request.EndDate)?.AddDays(1);
        if (startAt.HasValue && endAt.HasValue && startAt >= endAt)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "开始日期不能晚于结束日期");
        }

        return (storeId, startAt, endAt, ParseOptionalSettlementChannel(request.Channel));
    }

    private async Task EnsureStoreAccessibleAsync(long storeId, CancellationToken cancellationToken)
    {
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, storeId, cancellationToken);
    }

    private static DateTime ParseRequiredDate(string? value, string parameterName)
    {
        return ParseDateOrNull(value)
            ?? throw new BusinessException(ErrorCodes.BadRequest, $"{parameterName} 必填，格式为 yyyy-MM-dd");
    }

    private static DateTime? ParseDateOrNull(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        if (DateTime.TryParseExact(
                value,
                "yyyy-MM-dd",
                CultureInfo.InvariantCulture,
                DateTimeStyles.None,
                out var parsed))
        {
            return DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
        }

        throw new BusinessException(ErrorCodes.BadRequest, "日期格式必须为 yyyy-MM-dd");
    }

    private static PaymentMethod ParseRequiredSettlementChannel(string? channel)
    {
        return ParseOptionalSettlementChannel(channel)
            ?? throw new BusinessException(ErrorCodes.BadRequest, "channel 必填，仅支持 wechat 或 alipay");
    }

    private static PaymentMethod? ParseOptionalSettlementChannel(string? channel)
    {
        return (channel ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "wechat" => PaymentMethod.WeChatPay,
            "alipay" => PaymentMethod.Alipay,
            "" => null,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "channel 仅支持 wechat 或 alipay")
        };
    }

    private static FinanceSettlementListItemResponse MapListItem(FinanceSettlementListItemDto source)
    {
        return new FinanceSettlementListItemResponse
        {
            ArrivedDate = source.ArrivedDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
            Channel = source.Channel,
            ChannelText = source.ChannelText,
            TransactionCount = source.TransactionCount,
            ArrivedAmount = source.ArrivedAmount
        };
    }

    private static FinanceSettlementDetailItemResponse MapDetailItem(FinanceSettlementDetailItemDto source)
    {
        return new FinanceSettlementDetailItemResponse
        {
            OrderNo = source.OrderNo,
            Amount = source.Amount,
            PaidAt = source.PaidAt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)
        };
    }
}
