package com.starry.admin.modules.withdraw.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.withdraw.entity.EarningsBackfillLogEntity;
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
import com.starry.admin.modules.withdraw.entity.WithdrawalLogEntity;
import com.starry.admin.modules.withdraw.entity.WithdrawalRequestEntity;
import com.starry.admin.modules.withdraw.service.IEarningsBackfillLogService;
import com.starry.admin.modules.withdraw.service.IEarningsBackfillService;
import com.starry.admin.modules.withdraw.service.IEarningsService;
import com.starry.admin.modules.withdraw.service.ITenantAlipayConfigService;
import com.starry.admin.modules.withdraw.service.IWithdrawalLogService;
import com.starry.admin.modules.withdraw.service.IWithdrawalService;
import com.starry.admin.modules.withdraw.vo.ClerkEarningLineVo;
import com.starry.admin.modules.withdraw.vo.EarningsAdminQueryVo;
import com.starry.admin.modules.withdraw.vo.EarningsAdminSummaryVo;
import com.starry.admin.modules.withdraw.vo.EarningsBackfillRequest;
import com.starry.admin.modules.withdraw.vo.EarningsBackfillResponse;
import com.starry.admin.modules.withdraw.vo.WithdrawalRequestQueryVo;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.result.TypedR;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.*;

@Api(tags = "提现管理-后台")
@RestController
@RequestMapping("/admin/withdraw")
public class AdminWithdrawalController {

    @Resource
    private IWithdrawalService withdrawalService;
    @Resource
    private IWithdrawalLogService withdrawalLogService;
    @Resource
    private ITenantAlipayConfigService tenantAlipayConfigService;
    @Resource
    private IEarningsService earningsService;
    @Resource
    private IEarningsBackfillService earningsBackfillService;
    @Resource
    private IEarningsBackfillLogService backfillLogService;
    @Resource
    private IPlayOrderInfoService orderInfoService;
    @Resource
    private IPlayClerkUserInfoService clerkUserInfoService;
    @Resource
    private IPlayCustomUserInfoService customUserInfoService;

    @ApiOperation("分页查询提现请求")
    @PostMapping("/requests/listByPage")
    public TypedR<List<WithdrawalRequestEntity>> listRequests(@RequestBody WithdrawalRequestQueryVo vo) {
        LambdaQueryWrapper<WithdrawalRequestEntity> q = new LambdaQueryWrapper<>();
        if (vo.getClerkId() != null && !vo.getClerkId().isEmpty()) q.eq(WithdrawalRequestEntity::getClerkId, vo.getClerkId());
        if (vo.getStatus() != null && !vo.getStatus().isEmpty()) q.eq(WithdrawalRequestEntity::getStatus, vo.getStatus());
        q.eq(WithdrawalRequestEntity::getTenantId, SecurityUtils.getTenantId());
        q.orderByDesc(WithdrawalRequestEntity::getCreatedTime);
        IPage<WithdrawalRequestEntity> page = withdrawalService.page(new Page<>(vo.getPageNum(), vo.getPageSize()), q);
        return TypedR.okPage(page);
    }

    @ApiOperation("查询提现日志（按请求ID）")
    @GetMapping("/logs/list")
    public TypedR<List<WithdrawalLogEntity>> listLogs(@RequestParam("withdrawalId") String withdrawalId) {
        List<WithdrawalLogEntity> list = withdrawalLogService.lambdaQuery()
                .eq(WithdrawalLogEntity::getWithdrawalId, withdrawalId)
                .orderByAsc(WithdrawalLogEntity::getCreatedTime)
                .list();
        return TypedR.ok(list);
    }

    @ApiOperation("提现请求审计")
    @GetMapping("/requests/{id}/audit")
    public TypedR<List<ClerkEarningLineVo>> getRequestAudit(@PathVariable("id") String id) {
        String tenantId = SecurityUtils.getTenantId();
        WithdrawalRequestEntity request = withdrawalService.getById(id);
        if (request == null || !tenantId.equals(request.getTenantId())) {
            throw new CustomException("提现申请不存在或无权查看");
        }

        List<EarningsLineEntity> lines = earningsService.lambdaQuery()
                .eq(EarningsLineEntity::getTenantId, tenantId)
                .eq(EarningsLineEntity::getWithdrawalId, id)
                .orderByAsc(EarningsLineEntity::getCreatedTime)
                .list();
        if (lines.isEmpty()) {
            return TypedR.ok(Collections.emptyList());
        }

        List<String> orderIds = lines.stream()
                .map(EarningsLineEntity::getOrderId)
                .filter(orderId -> orderId != null && !orderId.isEmpty())
                .distinct()
                .collect(Collectors.toList());
        Map<String, PlayOrderInfoEntity> orderMap = orderIds.isEmpty()
                ? Collections.emptyMap()
                : orderInfoService.lambdaQuery()
                        .eq(PlayOrderInfoEntity::getTenantId, tenantId)
                        .in(PlayOrderInfoEntity::getId, orderIds)
                        .list()
                        .stream()
                        .collect(Collectors.toMap(PlayOrderInfoEntity::getId, Function.identity()));

        Map<String, PlayClerkUserInfoEntity> clerkMap = Collections.emptyMap();
        Map<String, PlayCustomUserInfoEntity> customerMap = Collections.emptyMap();
        if (!orderMap.isEmpty()) {
            List<String> clerkIds = orderMap.values().stream()
                    .map(PlayOrderInfoEntity::getAcceptBy)
                    .filter(clerkIdValue -> clerkIdValue != null && !clerkIdValue.isEmpty())
                    .distinct()
                    .collect(Collectors.toList());
            if (!clerkIds.isEmpty()) {
                clerkMap = clerkUserInfoService.lambdaQuery()
                        .eq(PlayClerkUserInfoEntity::getTenantId, tenantId)
                        .in(PlayClerkUserInfoEntity::getId, clerkIds)
                        .list()
                        .stream()
                        .collect(Collectors.toMap(PlayClerkUserInfoEntity::getId, Function.identity()));
            }

            List<String> customerIds = orderMap.values().stream()
                    .map(PlayOrderInfoEntity::getPurchaserBy)
                    .filter(customerIdValue -> customerIdValue != null && !customerIdValue.isEmpty())
                    .distinct()
                    .collect(Collectors.toList());
            if (!customerIds.isEmpty()) {
                customerMap = customUserInfoService.lambdaQuery()
                        .eq(PlayCustomUserInfoEntity::getTenantId, tenantId)
                        .in(PlayCustomUserInfoEntity::getId, customerIds)
                        .list()
                        .stream()
                        .collect(Collectors.toMap(PlayCustomUserInfoEntity::getId, Function.identity()));
            }
        }

        List<ClerkEarningLineVo> vos = new ArrayList<>(lines.size());
        for (EarningsLineEntity line : lines) {
            ClerkEarningLineVo vo = new ClerkEarningLineVo();
            vo.setId(line.getId());
            vo.setAmount(line.getAmount());
            vo.setStatus(line.getStatus());
            vo.setEarningType(line.getEarningType());
            vo.setWithdrawalId(line.getWithdrawalId());
            vo.setUnlockTime(line.getUnlockTime());
            vo.setCreatedTime(toLocalDateTime(line.getCreatedTime()));
            vo.setOrderId(line.getOrderId());
            if (line.getOrderId() != null) {
                PlayOrderInfoEntity order = orderMap.get(line.getOrderId());
                if (order != null) {
                    vo.setOrderNo(order.getOrderNo());
                    vo.setOrderStatus(order.getOrderStatus());
                    vo.setOrderEndTime(toLocalDateTime(order.getOrderEndTime()));
                    String clerkId = order.getAcceptBy();
                    if (clerkId != null && !clerkId.isEmpty()) {
                        vo.setOrderClerkId(clerkId);
                        PlayClerkUserInfoEntity clerk = clerkMap.get(clerkId);
                        if (clerk != null) {
                            vo.setOrderClerkNickname(clerk.getNickname());
                        }
                    }
                    String customerId = order.getPurchaserBy();
                    if (customerId != null && !customerId.isEmpty()) {
                        vo.setOrderCustomerId(customerId);
                        PlayCustomUserInfoEntity customer = customerMap.get(customerId);
                        if (customer != null) {
                            vo.setOrderCustomerNickname(customer.getNickname());
                        }
                    }
                }
            }
            vos.add(vo);
        }
        return TypedR.ok(vos);
    }

    @ApiOperation("分页查询收益明细")
    @PostMapping("/earnings/listByPage")
    public TypedR<List<EarningsLineEntity>> listEarnings(@RequestBody EarningsAdminQueryVo vo) {
        LambdaQueryWrapper<EarningsLineEntity> q = buildEarningsWrapper(vo);
        IPage<EarningsLineEntity> page = earningsService.page(new Page<>(vo.getPageNum(), vo.getPageSize()), q);
        return TypedR.okPage(page);
    }

    @ApiOperation("收益汇总")
    @GetMapping("/earnings/summary")
    public TypedR<EarningsAdminSummaryVo> summarize(EarningsAdminQueryVo vo) {
        LambdaQueryWrapper<EarningsLineEntity> q = buildEarningsWrapper(vo);
        List<EarningsLineEntity> records = earningsService.list(q);

        EarningsAdminSummaryVo summary = new EarningsAdminSummaryVo();
        Map<String, EarningsAdminSummaryVo.StatusStat> statMap = new HashMap<>();

        BigDecimal available = BigDecimal.ZERO;
        BigDecimal pending = BigDecimal.ZERO;
        BigDecimal withdrawn = BigDecimal.ZERO;
        BigDecimal total = BigDecimal.ZERO;

        for (EarningsLineEntity record : records) {
            BigDecimal amount = record.getAmount() == null ? BigDecimal.ZERO : record.getAmount();
            total = total.add(amount);
            String status = record.getStatus();
            EarningsAdminSummaryVo.StatusStat stat = statMap.computeIfAbsent(status, k -> {
                EarningsAdminSummaryVo.StatusStat st = new EarningsAdminSummaryVo.StatusStat();
                st.setStatus(k);
                return st;
            });
            stat.setCount(stat.getCount() + 1);
            stat.setAmount(stat.getAmount().add(amount));

            if ("available".equals(status)) {
                available = available.add(amount);
            } else if ("withdrawn".equals(status)) {
                withdrawn = withdrawn.add(amount);
            } else if ("frozen".equals(status) || "withdrawing".equals(status)) {
                pending = pending.add(amount);
            }
        }

        summary.setAvailableAmount(available);
        summary.setPendingAmount(pending);
        summary.setWithdrawnAmount(withdrawn);
        summary.setTotalAmount(total);
        summary.setStatusStats(new ArrayList<>(statMap.values()));
        return TypedR.ok(summary);
    }

    @ApiOperation("收益补算（试运行或执行）")
    @PostMapping("/earnings/backfill")
    public TypedR<EarningsBackfillResponse> backfill(@RequestBody EarningsBackfillRequest request) {
        return TypedR.ok(earningsBackfillService.backfill(request));
    }

    @ApiOperation("收益补算历史记录")
    @GetMapping("/earnings/backfill/logs")
    public TypedR<List<EarningsBackfillLogEntity>> listBackfillLogs() {
        List<EarningsBackfillLogEntity> logs = backfillLogService.lambdaQuery()
                .eq(EarningsBackfillLogEntity::getTenantId, SecurityUtils.getTenantId())
                .orderByDesc(EarningsBackfillLogEntity::getCreatedTime)
                .last("limit 20")
                .list();
        return TypedR.ok(logs);
    }

    @ApiOperation("查询当前租户是否已配置支付宝")
    @GetMapping("/alipay/config/present")
    public TypedR<Boolean> hasAlipayConfig() {
        String tenantId = SecurityUtils.getTenantId();
        return TypedR.ok(tenantAlipayConfigService.hasConfig(tenantId));
    }

    @ApiOperation("手动标记打款成功")
    @PostMapping("/requests/{id}/manual/success")
    public TypedR<Void> manualSuccess(@PathVariable("id") String id, @RequestParam(value = "operator", required = false) String operator) {
        withdrawalService.markManualSuccess(id, operator);
        return TypedR.ok(null);
    }

    @ApiOperation("支付宝自动打款（若已配置）")
    @PostMapping("/requests/{id}/auto")
    public TypedR<Void> autoPayout(@PathVariable("id") String id) {
        withdrawalService.autoPayout(id);
        return TypedR.ok(null);
    }

    private LambdaQueryWrapper<EarningsLineEntity> buildEarningsWrapper(EarningsAdminQueryVo vo) {
        LambdaQueryWrapper<EarningsLineEntity> q = new LambdaQueryWrapper<>();
        q.eq(EarningsLineEntity::getTenantId, SecurityUtils.getTenantId());
        if (vo.getClerkId() != null && !vo.getClerkId().isEmpty()) {
            q.eq(EarningsLineEntity::getClerkId, vo.getClerkId());
        }
        if (vo.getEarningType() != null && !vo.getEarningType().isEmpty()) {
            q.eq(EarningsLineEntity::getEarningType, vo.getEarningType());
        }
        if (vo.getStatus() != null && !vo.getStatus().isEmpty()) {
            q.eq(EarningsLineEntity::getStatus, vo.getStatus());
        }
        if (vo.getBeginTime() != null && !vo.getBeginTime().isEmpty()) {
            q.ge(EarningsLineEntity::getCreatedTime, LocalDateTime.parse(vo.getBeginTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
        if (vo.getEndTime() != null && !vo.getEndTime().isEmpty()) {
            q.le(EarningsLineEntity::getCreatedTime, LocalDateTime.parse(vo.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
        q.orderByDesc(EarningsLineEntity::getCreatedTime);
        return q;
    }

    private LocalDateTime toLocalDateTime(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof LocalDateTime) {
            return (LocalDateTime) value;
        }
        if (value instanceof Date) {
            return ((Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        }
        return null;
    }
}
