package com.jeesharp.business.food.controller;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordBizSceneEnum;
import com.jeesharp.business.modules.food.application.FoodBookingOrderApplicationService;
import com.jeesharp.business.modules.food.application.FoodPackageApplicationService;
import com.jeesharp.business.modules.food.application.FoodRestaurantApplicationService;
import com.jeesharp.business.modules.food.dto.FoodBookingCreateDTO;
import com.jeesharp.business.modules.food.dto.FoodBookingRefundApplyDTO;
import com.jeesharp.business.modules.food.dto.FoodCancelBookingDTO;
import com.jeesharp.business.modules.food.dto.FoodConfirmPayDTO;
import com.jeesharp.business.modules.food.dto.FoodPreparePayDTO;
import com.jeesharp.business.modules.food.enums.FoodBookingStatusEnum;
import com.jeesharp.business.modules.common.enums.OnlineEnabledEnum;
import com.jeesharp.business.modules.food.vo.FoodBookingOrderQueryVO;
import com.jeesharp.business.modules.food.vo.FoodBookingResultVO;
import com.jeesharp.business.modules.food.vo.FoodPackageInfoVO;
import com.jeesharp.business.modules.food.vo.FoodPayPrepareVO;
import com.jeesharp.business.modules.food.vo.FoodPayStatusVO;
import com.jeesharp.business.modules.food.vo.FoodRestaurantInfoVO;
import com.jeesharp.business.pay.model.WechatPrepayCommand;
import com.jeesharp.business.pay.model.WechatPrepayOutcome;
import com.jeesharp.business.pay.service.WechatJsapiPayOrderPrepayService;
import com.jeesharp.framework.memberauth.utils.MemberAuthUtils;
import com.jeesharp.framework.memberauth.web.BaseMobileController;
import com.jeesharp.framework.utils.RequestUtils;
import com.jeesharp.framework.pojo.AjaxResult;
import com.jeesharp.framework.pojo.query.PageRespVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 小程序美食团购 HTTP 入口（{@code /mobile/food}）。
 *
 * <p>典型顺序：{@code booking/create} → {@code booking/pay/prepare} → 微信回调
 * → {@code FoodWechatPayCallbackAdapter}；到店核销后统一单 markClosed。</p>
 *
 * @author jeesharp
 * @since 2026-05-08
 */
@RestController
@RequestMapping("${jeesharp.web.mobilePath}/food")
@Tag(name = "小程序-美食")
@AllArgsConstructor
public class FoodMobileController extends BaseMobileController {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private final FoodRestaurantApplicationService foodRestaurantApplicationService;
    private final FoodPackageApplicationService foodPackageApplicationService;
    private final FoodBookingOrderApplicationService foodBookingOrderApplicationService;
    private final WechatJsapiPayOrderPrepayService wechatJsapiPayOrderPrepayService;

    @Operation(summary = "门店列表")
    @GetMapping("/restaurants")
    public AjaxResult<List<Map<String, Object>>> restaurants() {
        List<FoodRestaurantInfoVO> restaurants = foodRestaurantApplicationService.listOnline();
        return AjaxResult.okData(restaurants.stream().map(this::toRestaurantRow).toList());
    }

    @Operation(summary = "门店详情")
    @GetMapping("/restaurant/{id}")
    public AjaxResult<Map<String, Object>> restaurant(@PathVariable("id") String id) {
        FoodRestaurantInfoVO restaurant = foodRestaurantApplicationService.getOnline(id);
        if (restaurant == null) {
            return AjaxResult.failMsg("门店不存在");
        }
        return AjaxResult.okData(toRestaurantRow(restaurant));
    }

    @Operation(summary = "门店套餐")
    @GetMapping("/packages/{restaurantId}")
    public AjaxResult<List<Map<String, Object>>> packages(@PathVariable("restaurantId") String restaurantId) {
        List<FoodPackageInfoVO> packages = foodPackageApplicationService.listOnlineByRestaurant(restaurantId);
        return AjaxResult.okData(packages.stream().map(this::toPackageRow).toList());
    }

    /**
     * 小程序创建美食预订入口。
     * <p>链路：注入会员 ID → {@link FoodBookingOrderApplicationService#createBooking} → 转为前端 Map 返回。</p>
     */
    @Operation(summary = "创建预订")
    @PostMapping("/booking/create")
    public AjaxResult<Map<String, Object>> bookingCreate(@RequestBody @Valid FoodBookingCreateDTO dto) {
        // 会员 ID 只认 Token，不信任小程序请求体传入的 memberId
        dto.setMemberId(MemberAuthUtils.getLoginUserId());
        FoodBookingResultVO vo = foodBookingOrderApplicationService.createBooking(dto);
        // 合并 VO 字段与 resultPayloadJson，供成功页/分享页展示
        return AjaxResult.okData(toBookingResult(vo));
    }

    @Operation(summary = "预支付")
    @PostMapping("/booking/pay/prepare")
    public AjaxResult<FoodPayPrepareVO> payPrepare(@RequestBody @Valid FoodPreparePayDTO dto) throws Exception {
        FoodPayPrepareVO vo = foodBookingOrderApplicationService.preparePay(dto);
        String memberId = MemberAuthUtils.getLoginUserId();
        long amountFen = vo.getAmountFen() == null ? 0L : vo.getAmountFen();
        WechatPrepayCommand cmd = WechatPrepayCommand.builder()
                .bizScene(PayRecordBizSceneEnum.FOOD_ORDER.getCode())
                .bizOrderId(vo.getOrderId())
                .amountFen(amountFen)
                .description(vo.getDescription())
                .attach(vo.getAttach())
                .memberId(memberId)
                .appId(RequestUtils.getAppID())
                .tenantId(vo.getTenantId())
                .build();
        WechatPrepayOutcome outcome = wechatJsapiPayOrderPrepayService.execute(cmd);
        wechatJsapiPayOrderPrepayService.applyPrepare(vo, outcome);
        return AjaxResult.okData(vo);
    }

    @Operation(summary = "支付状态")
    @GetMapping("/booking/pay/status/{orderId}")
    public AjaxResult<FoodPayStatusVO> payStatus(@PathVariable("orderId") String orderId) {
        FoodPayStatusVO vo = foodBookingOrderApplicationService.getPayStatus(orderId);
        if (vo == null) {
            return AjaxResult.failMsg("订单不存在");
        }
        return AjaxResult.okData(vo);
    }

    @Operation(summary = "模拟支付成功 / 支付回调确认")
    @PostMapping("/booking/pay/mock-success")
    public AjaxResult<Map<String, Object>> payMockSuccess(@RequestBody @Valid FoodConfirmPayDTO dto) {
        FoodBookingResultVO vo = foodBookingOrderApplicationService.confirmPaySuccess(dto);
        return AjaxResult.okData(toBookingResult(vo));
    }

    @Operation(summary = "取消预订")
    @PostMapping("/booking/cancel")
    public AjaxResult<Void> cancel(@RequestBody @Valid FoodCancelBookingDTO dto) {
        foodBookingOrderApplicationService.cancelBooking(dto);
        return AjaxResult.ok("取消成功");
    }

    @Operation(summary = "申请退款（已支付未核销）")
    @PostMapping("/booking/refund/apply")
    public AjaxResult<String> refundApply(@RequestBody @Valid FoodBookingRefundApplyDTO dto) {
        String memberId = MemberAuthUtils.getLoginUserId();
        String payRefundOrderId = foodBookingOrderApplicationService.applyBookingRefund(dto, memberId);
        return AjaxResult.okData(payRefundOrderId);
    }

    @Operation(summary = "我的美食预订（分页）")
    @PostMapping("/booking/myList")
    public AjaxResult<PageRespVO<Map<String, Object>>> bookingMyList(@RequestBody(required = false) FoodBookingOrderQueryVO query) {
        String memberId = MemberAuthUtils.getLoginUserId();
        PageRespVO<FoodBookingResultVO> page = foodBookingOrderApplicationService.findMyBookingsForMobile(memberId, query);
        PageRespVO<Map<String, Object>> out = new PageRespVO<>();
        out.setTotal(page.getTotal());
        out.setPageNo(page.getPageNo());
        out.setPageSize(page.getPageSize());
        out.setRecords(page.getRecords().stream().map(this::toBookingResult).toList());
        return AjaxResult.okData(out);
    }

    @Operation(summary = "预订结果")
    @GetMapping("/booking/result/{orderId}")
    public AjaxResult<Map<String, Object>> bookingResult(@PathVariable("orderId") String orderId) {
        String memberId = null;
        try {
            memberId = MemberAuthUtils.getLoginUserId();
        } catch (Exception ignored) {
            // 分享链接未登录
        }
        FoodBookingResultVO vo = foodBookingOrderApplicationService.getBookingResultForMobileByAnyId(orderId, memberId);
        if (vo == null) {
            return AjaxResult.failMsg("订单不存在");
        }
        return AjaxResult.okData(toBookingResult(vo));
    }

    private Map<String, Object> toRestaurantRow(FoodRestaurantInfoVO restaurant) {
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("id", restaurant.getId());
        row.put("name", stringValue(restaurant.getName()));
        row.put("category", stringValue(restaurant.getCategory()));
        row.put("rating", formatRating(restaurant.getRating()));
        row.put("locationLabel", stringValue(restaurant.getLocationLabel()));
        row.put("address", stringValue(restaurant.getAddress()));
        row.put("phone", stringValue(restaurant.getPhone()));
        row.put("distanceLabel", stringValue(restaurant.getDistanceLabel()));
        if (restaurant.getLatitude() != null) {
            row.put("latitude", restaurant.getLatitude());
        }
        if (restaurant.getLongitude() != null) {
            row.put("longitude", restaurant.getLongitude());
        }
        row.put("openHours", stringValue(restaurant.getOpenHours()));
        row.put("cover", stringValue(restaurant.getCover()));
        row.put("gallery", parseStringList(restaurant.getGalleryJson()));
        row.put("tags", parseStringList(restaurant.getTagsJson()));
        row.put("notice", parseStringList(restaurant.getNoticeJson()));
        row.put("policy", parseObject(restaurant.getPolicyJson()));
        row.put("packages", foodPackageApplicationService.listOnlineByRestaurant(restaurant.getId()).stream().map(this::toPackageRow).toList());
        return row;
    }

    private Map<String, Object> toPackageRow(FoodPackageInfoVO foodPackage) {
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("id", foodPackage.getId());
        row.put("name", stringValue(foodPackage.getName()));
        row.put("subtitle", stringValue(foodPackage.getSubtitle()));
        row.put("price", money(foodPackage.getPrice()));
        row.put("originalPrice", money(foodPackage.getOriginalPrice()));
        row.put("includes", parseStringList(foodPackage.getIncludesJson()));
        row.put("images", parseStringList(foodPackage.getImagesJson()));
        row.put("stockHint", stringValue(foodPackage.getStockHint()));
        Integer onlineOrderEnabled = foodPackage.getOnlineOrderEnabled();
        row.put("onlineOrderEnabled",
                onlineOrderEnabled != null ? onlineOrderEnabled : OnlineEnabledEnum.ENABLED.getValue());
        row.put("maxQuantityPerOrder", foodPackage.getMaxQuantityPerOrder());
        row.put("availableQuantity", foodPackage.getAvailableQuantity());
        return row;
    }

    /**
     * 将预订结果 VO 转为小程序约定的 Map（orderId、核销码、金额文案、统一单号等）。
     */
    private Map<String, Object> toBookingResult(FoodBookingResultVO vo) {
        Map<String, Object> row = parseObject(vo.getResultPayloadJson());
        if (StrUtil.isNotBlank(vo.getOrderId())) {
            row.put("orderId", vo.getOrderId());
        }
        if (StrUtil.isNotBlank(vo.getUnifiedOrderId())) {
            row.put("unifiedOrderId", vo.getUnifiedOrderId());
        }
        row.putIfAbsent("verifyCode", stringValue(vo.getVerifyCode()));
        if (StrUtil.isNotBlank(vo.getTitle())) {
            row.put("title", vo.getTitle());
        } else {
            row.putIfAbsent("title", "美食团购订单");
        }
        if (StrUtil.isNotBlank(vo.getAmountLabel())) {
            row.put("amountLabel", vo.getAmountLabel());
        } else {
            row.putIfAbsent("amountLabel", "¥0");
        }
        if (StrUtil.isNotBlank(vo.getStatusLabel())) {
            row.put("statusLabel", vo.getStatusLabel());
        } else {
            row.putIfAbsent("statusLabel", mapBookingStatus(vo.getStatus()));
        }
        if (StrUtil.isNotBlank(vo.getPayStatus())) {
            row.put("payStatus", vo.getPayStatus());
        }
        if (StrUtil.isNotBlank(vo.getCreatedAt())) {
            row.put("createdAt", vo.getCreatedAt());
        } else {
            row.putIfAbsent("createdAt", "");
        }
        return row;
    }

    private String mapBookingStatus(String status) {
        String desc = FoodBookingStatusEnum.getDescByValue(status);
        return StrUtil.isNotBlank(desc) ? desc : "待使用";
    }

    private List<String> parseStringList(String json) {
        if (json == null || json.isBlank()) {
            return Collections.emptyList();
        }
        try {
            return OBJECT_MAPPER.readValue(json, new TypeReference<List<String>>() { });
        } catch (Exception ex) {
            return Collections.emptyList();
        }
    }

    private Map<String, Object> parseObject(String json) {
        if (json == null || json.isBlank() || "{}".equals(json.trim())) {
            return new LinkedHashMap<>();
        }
        try {
            return OBJECT_MAPPER.readValue(json, new TypeReference<LinkedHashMap<String, Object>>() { });
        } catch (Exception ex) {
            return new LinkedHashMap<>();
        }
    }

    private String formatRating(BigDecimal rating) {
        if (rating == null) {
            return "";
        }
        return rating.setScale(1, RoundingMode.HALF_UP).toPlainString();
    }

    /**
     * 金额（元），保留两位小数；禁止取整，否则 0.01 元会在小程序展示为 0。
     */
    private BigDecimal money(BigDecimal value) {
        if (value == null) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        return value.setScale(2, RoundingMode.HALF_UP);
    }

    private String stringValue(String value) {
        return value != null ? value : "";
    }
}
