package com.jeesharp.business.food.controller;

import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordBizSceneEnum;
import com.jeesharp.business.modules.food.application.FoodCompanyJoinApplicationService;
import com.jeesharp.business.modules.food.application.FoodCompanyMealApplicationService;
import com.jeesharp.business.modules.food.application.FoodPersonalMealApplicationService;
import com.jeesharp.business.modules.food.dto.*;
import com.jeesharp.business.modules.food.vo.*;
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.pojo.AjaxResult;
import com.jeesharp.framework.pojo.query.PageRespVO;
import com.jeesharp.framework.utils.RequestUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;

/**
 * 小程序个人/企业订餐 HTTP 入口（{@code /mobile/food/meal}）。
 *
 * <p>个人订餐：选菜 → create → pay/prepare → {@code FoodMealWechatPayCallbackAdapter} → handlePaid；
 * 退款走 applyRefund + 微信退款回调 handleRefundSuccess。</p>
 * <p>编排见 {@link com.jeesharp.business.modules.food.application.FoodPersonalMealApplicationService}。</p>
 *
 * @author jeesharp
 * @since 2026-06-09
 */
@RestController
@RequestMapping("${jeesharp.web.mobilePath}/food/meal")
@Tag(name = "小程序-订餐")
@AllArgsConstructor
@Slf4j
public class FoodMealMobileController extends BaseMobileController {

    private final FoodCompanyMealApplicationService foodCompanyMealApplicationService;
    private final FoodCompanyJoinApplicationService foodCompanyJoinApplicationService;
    private final FoodPersonalMealApplicationService foodPersonalMealApplicationService;
    private final WechatJsapiPayOrderPrepayService wechatJsapiPayOrderPrepayService;
    private final org.springframework.core.env.Environment environment;

    @Operation(summary = "订餐服务区域")
    @GetMapping("/serviceAreas")
    public AjaxResult<List<FoodMealRestaurantVO>> serviceAreas() {
        return AjaxResult.okData(foodPersonalMealApplicationService.listMealRestaurants());
    }

    @Operation(summary = "订餐服务区域")
    @GetMapping("/restaurants")
    public AjaxResult<List<FoodMealRestaurantVO>> restaurants() {
        return AjaxResult.okData(foodPersonalMealApplicationService.listMealRestaurants());
    }

    @Operation(summary = "个人订餐可订日期")
    @GetMapping("/dates")
    public AjaxResult<List<LocalDate>> dates(
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId) {
        return AjaxResult.okData(foodPersonalMealApplicationService.listBookableDates(areaId(serviceAreaId, restaurantId)));
    }

    @Operation(summary = "个人订餐日期状态")
    @GetMapping("/dateStatuses")
    public AjaxResult<List<FoodMealDateStatusVO>> dateStatuses(
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam(value = "weekStart", required = false) LocalDate weekStart) {
        return AjaxResult.okData(foodPersonalMealApplicationService.listDateStatuses(areaId(serviceAreaId, restaurantId), weekStart));
    }

    @Operation(summary = "个人订餐餐别")
    @GetMapping("/mealTypes")
    public AjaxResult<List<Map<String, String>>> mealTypes(
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam(value = "mealDate", required = false) LocalDate mealDate) {
        return AjaxResult.okData(foodPersonalMealApplicationService.listMealTypes(areaId(serviceAreaId, restaurantId), mealDate));
    }

    @Operation(summary = "个人订餐菜单")
    @GetMapping("/menu")
    public AjaxResult<FoodPersonalMealMenuVO> menu(
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam("mealDate") LocalDate mealDate,
        @RequestParam("mealType") String mealType) {
        FoodPersonalMealMenuVO vo = foodPersonalMealApplicationService.getMenu(areaId(serviceAreaId, restaurantId), mealDate, mealType);
        return vo == null ? AjaxResult.failMsg("暂无可订菜单") : AjaxResult.okData(vo);
    }

    @Operation(summary = "订餐热门菜品")
    @GetMapping("/hotItems")
    public AjaxResult<List<FoodMealHotItemVO>> hotItems(
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam(value = "mealType", required = false) String mealType,
        @RequestParam(value = "limit", required = false) Integer limit) {
        return AjaxResult.okData(foodPersonalMealApplicationService.listHotItems(areaId(serviceAreaId, restaurantId), mealType, limit));
    }

    @Operation(summary = "个人订餐菜单项详情")
    @GetMapping("/item/{menuItemId}")
    public AjaxResult<FoodMealMenuItemListVO> menuItemDetail(@PathVariable("menuItemId") String menuItemId) {
        FoodMealMenuItemListVO vo = foodPersonalMealApplicationService.getMenuItemDetail(menuItemId);
        return vo == null ? AjaxResult.failMsg("菜品不存在或已下架") : AjaxResult.okData(vo);
    }

    @Operation(summary = "个人订餐试算")
    @PostMapping("/order/trial")
    public AjaxResult<FoodPersonalMealTrialVO> trialPersonalMealOrder(@RequestBody @Valid FoodPersonalMealOrderCreateDTO dto) {
        dto.setMemberId(MemberAuthUtils.getLoginUserId());
        return AjaxResult.okData(foodPersonalMealApplicationService.trial(dto));
    }

    @Operation(summary = "个人订餐下单")
    @PostMapping("/order/create")
    public AjaxResult<FoodPersonalMealOrderVO> createPersonalMealOrder(@RequestBody @Valid FoodPersonalMealOrderCreateDTO dto) {
        dto.setMemberId(MemberAuthUtils.getLoginUserId());
        return AjaxResult.okData(foodPersonalMealApplicationService.createOrder(dto));
    }

    @Operation(summary = "个人订餐预支付")
    @PostMapping("/order/pay/prepare")
    public AjaxResult<FoodPersonalMealPayPrepareVO> preparePersonalMealPay(@RequestBody @Valid FoodPersonalMealPayPrepareDTO dto) throws Exception {
        String memberId = MemberAuthUtils.getLoginUserId();
        FoodPersonalMealPayPrepareVO vo = foodPersonalMealApplicationService.preparePay(dto.getOrderId(), memberId);
        if (vo == null) {
            return AjaxResult.failMsg("订单不存在");
        }
        if (Boolean.parseBoolean(environment.getProperty("jeesharp.food.meal.skip-payment", "false"))) {
            FoodPersonalMealOrderPaidDTO paidDTO = new FoodPersonalMealOrderPaidDTO();
            paidDTO.setOrderId(vo.getOrderId());
            paidDTO.setPaidAmount(vo.getAmountFen() == null ? java.math.BigDecimal.ZERO : new java.math.BigDecimal(vo.getAmountFen()).movePointLeft(2));
            try {
                foodPersonalMealApplicationService.handlePaid(paidDTO);
            } catch (Exception ex) {
                log.warn("skip-payment 模式标记已支付失败 orderId={}: {}", vo.getOrderId(), ex.getMessage());
            }
            return AjaxResult.okData(vo);
        }
        WechatPrepayCommand cmd = WechatPrepayCommand.builder()
            .bizScene(PayRecordBizSceneEnum.FOOD_MEAL_ORDER.getCode())
            .bizOrderId(vo.getOrderId())
            .amountFen(vo.getAmountFen() == null ? 0L : vo.getAmountFen())
            .description(vo.getDescription())
            .memberId(memberId)
            .appId(RequestUtils.getAppID())
            .tenantId(vo.getTenantId())
            .build();
        WechatPrepayOutcome outcome = wechatJsapiPayOrderPrepayService.execute(cmd);
        wechatJsapiPayOrderPrepayService.applyPrepare(vo, outcome);
        foodPersonalMealApplicationService.bindPayOrder(vo.getOrderId(), memberId, vo.getPayOrderId(), vo.getOutTradeNo());
        return AjaxResult.okData(vo);
    }

    @Operation(summary = "个人订餐待支付取消")
    @PostMapping("/order/cancel")
    public AjaxResult<FoodPersonalMealOrderVO> cancelPersonalMealOrder(@RequestBody @Valid FoodPersonalMealOrderCancelDTO dto) {
        return AjaxResult.okData(foodPersonalMealApplicationService.cancel(dto, MemberAuthUtils.getLoginUserId()));
    }

    @Operation(summary = "个人订餐退款申请")
    @PostMapping("/order/refund/apply")
    public AjaxResult<FoodPersonalMealOrderVO> applyPersonalMealRefund(@RequestBody @Valid FoodMealOrderRefundApplyDTO dto) {
        return AjaxResult.okData(foodPersonalMealApplicationService.applyRefund(dto, MemberAuthUtils.getLoginUserId()));
    }

    @Operation(summary = "我的个人订餐订单")
    @PostMapping("/order/myList")
    public AjaxResult<PageRespVO<FoodPersonalMealOrderVO>> personalMealOrderList(@RequestBody(required = false) FoodMealOrderQueryVO query) {
        return AjaxResult.okData(foodPersonalMealApplicationService.findMyOrders(MemberAuthUtils.getLoginUserId(), query));
    }

    @Operation(summary = "个人订餐订单详情")
    @GetMapping("/order/detail/{orderId}")
    public AjaxResult<FoodPersonalMealOrderVO> personalMealOrderDetail(@PathVariable("orderId") String orderId) {
        FoodPersonalMealOrderVO vo = foodPersonalMealApplicationService.getOrderDetail(orderId, MemberAuthUtils.getLoginUserId());
        return vo == null ? AjaxResult.failMsg("订单不存在") : AjaxResult.okData(vo);
    }

    @Operation(summary = "我的企业列表")
    @GetMapping("/company/myCompanies")
    public AjaxResult<List<FoodCompanyEmployeeCompanyVO>> myCompanies() {
        return AjaxResult.okData(foodCompanyMealApplicationService.listMyCompanies(MemberAuthUtils.getLoginUserId()));
    }

    @Operation(summary = "搜索可申请企业")
    @GetMapping("/company/search")
    public AjaxResult<List<FoodCompanySearchVO>> searchCompanies(@RequestParam(value = "keyword", required = false) String keyword) {
        return AjaxResult.okData(foodCompanyJoinApplicationService.searchCompanies(keyword));
    }

    @Operation(summary = "申请加入企业")
    @PostMapping("/company/join/apply")
    public AjaxResult<FoodCompanyJoinApplyVO> applyJoinCompany(@RequestBody @Valid FoodCompanyJoinApplyCreateDTO dto) {
        dto.setMemberId(MemberAuthUtils.getLoginUserId());
        return AjaxResult.okData(foodCompanyJoinApplicationService.submitApply(dto));
    }

    @Operation(summary = "我的企业加入申请")
    @GetMapping("/company/join/myApplications")
    public AjaxResult<List<FoodCompanyJoinApplyVO>> myCompanyJoinApplications() {
        return AjaxResult.okData(foodCompanyJoinApplicationService.listMyApplications(MemberAuthUtils.getLoginUserId()));
    }

    @Operation(summary = "企业团餐权益")
    @GetMapping("/company/benefit")
    public AjaxResult<FoodCompanyMealBenefitVO> benefit(@RequestParam("companyId") String companyId) {
        String memberId = MemberAuthUtils.getLoginUserId();
        return AjaxResult.okData(foodCompanyMealApplicationService.getBenefit(memberId, companyId));
    }

    @Operation(summary = "企业补贴规则")
    @GetMapping("/company/subsidyRules")
    public AjaxResult<List<FoodCompanyMealTrialVO.FoodCompanySubsidyRuleSummaryVO>> subsidyRules(
        @RequestParam("companyId") String companyId,
        @RequestParam(value = "mealType", required = false) String mealType) {
        String memberId = MemberAuthUtils.getLoginUserId();
        return AjaxResult.okData(foodCompanyMealApplicationService.listSubsidyRules(memberId, companyId, mealType));
    }

    @Operation(summary = "企业团餐可订日期")
    @GetMapping("/company/dates")
    public AjaxResult<List<LocalDate>> companyDates(
        @RequestParam("companyId") String companyId,
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId) {
        return AjaxResult.okData(foodCompanyMealApplicationService.listBookableDates(MemberAuthUtils.getLoginUserId(), companyId, areaId(serviceAreaId, restaurantId)));
    }

    @Operation(summary = "企业团餐日期状态")
    @GetMapping("/company/dateStatuses")
    public AjaxResult<List<FoodMealDateStatusVO>> companyDateStatuses(
        @RequestParam("companyId") String companyId,
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam(value = "weekStart", required = false) LocalDate weekStart) {
        return AjaxResult.okData(foodCompanyMealApplicationService.listDateStatuses(MemberAuthUtils.getLoginUserId(), companyId, areaId(serviceAreaId, restaurantId), weekStart));
    }

    @Operation(summary = "企业团餐餐别")
    @GetMapping("/company/mealTypes")
    public AjaxResult<List<Map<String, String>>> companyMealTypes(
        @RequestParam("companyId") String companyId,
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam(value = "mealDate", required = false) LocalDate mealDate) {
        return AjaxResult.okData(foodCompanyMealApplicationService.listMealTypes(MemberAuthUtils.getLoginUserId(), companyId, areaId(serviceAreaId, restaurantId), mealDate));
    }

    @Operation(summary = "企业团餐菜单")
    @GetMapping("/company/menu")
    public AjaxResult<FoodPersonalMealMenuVO> companyMenu(
        @RequestParam("companyId") String companyId,
        @RequestParam(value = "serviceAreaId", required = false) String serviceAreaId,
        @RequestParam(value = "restaurantId", required = false) String restaurantId,
        @RequestParam("mealDate") LocalDate mealDate,
        @RequestParam("mealType") String mealType) {
        FoodPersonalMealMenuVO vo = foodCompanyMealApplicationService.getMenu(MemberAuthUtils.getLoginUserId(), companyId, areaId(serviceAreaId, restaurantId), mealDate, mealType);
        return vo == null ? AjaxResult.failMsg("暂无可订菜单") : AjaxResult.okData(vo);
    }

    @Operation(summary = "企业团餐菜单项详情")
    @GetMapping("/company/item/{menuItemId}")
    public AjaxResult<FoodMealMenuItemListVO> companyMenuItemDetail(
        @PathVariable("menuItemId") String menuItemId,
        @RequestParam("companyId") String companyId) {
        FoodMealMenuItemListVO vo = foodCompanyMealApplicationService.getMenuItemDetail(MemberAuthUtils.getLoginUserId(), companyId, menuItemId);
        return vo == null ? AjaxResult.failMsg("菜品不存在或已下架") : AjaxResult.okData(vo);
    }

    private String areaId(String serviceAreaId, String restaurantId) {
        return StrUtil.blankToDefault(serviceAreaId, restaurantId);
    }

    @Operation(summary = "企业团餐试算")
    @PostMapping("/company/trial")
    public AjaxResult<FoodCompanyMealTrialVO> trial(@RequestBody @Valid FoodCompanyMealTrialDTO dto) {
        dto.setMemberId(MemberAuthUtils.getLoginUserId());
        return AjaxResult.okData(foodCompanyMealApplicationService.trial(dto));
    }

    @Operation(summary = "企业团餐下单")
    @PostMapping("/company/order/create")
    public AjaxResult<FoodCompanyMealOrderResultVO> createCompanyMealOrder(@RequestBody @Valid FoodCompanyMealOrderCreateDTO dto) {
        dto.setMemberId(MemberAuthUtils.getLoginUserId());
        return AjaxResult.okData(foodCompanyMealApplicationService.createCompanyMealOrder(dto));
    }

    @Operation(summary = "企业团餐补差预支付")
    @PostMapping("/company/order/pay/prepare")
    public AjaxResult<FoodPersonalMealPayPrepareVO> prepareCompanyMealPay(@RequestBody @Valid FoodPersonalMealPayPrepareDTO dto) throws Exception {
        String memberId = MemberAuthUtils.getLoginUserId();
        FoodPersonalMealPayPrepareVO vo = foodCompanyMealApplicationService.preparePay(dto.getOrderId(), memberId);
        if (vo == null) {
            return AjaxResult.failMsg("订单不存在");
        }
        WechatPrepayCommand cmd = WechatPrepayCommand.builder()
            .bizScene(PayRecordBizSceneEnum.FOOD_COMPANY_MEAL_ORDER.getCode())
            .bizOrderId(vo.getOrderId())
            .amountFen(vo.getAmountFen() == null ? 0L : vo.getAmountFen())
            .description(vo.getDescription())
            .memberId(memberId)
            .appId(RequestUtils.getAppID())
            .tenantId(vo.getTenantId())
            .build();
        WechatPrepayOutcome outcome = wechatJsapiPayOrderPrepayService.execute(cmd);
        wechatJsapiPayOrderPrepayService.applyPrepare(vo, outcome);
        foodCompanyMealApplicationService.bindCompanyPayOrder(vo.getOrderId(), memberId, vo.getPayOrderId(), vo.getOutTradeNo());
        return AjaxResult.okData(vo);
    }

    @Operation(summary = "企业团餐待支付取消")
    @PostMapping("/company/order/cancel")
    public AjaxResult<FoodCompanyMealOrderResultVO> cancelCompanyMealOrder(@RequestBody @Valid FoodPersonalMealOrderCancelDTO dto) {
        return AjaxResult.okData(foodCompanyMealApplicationService.cancel(dto, MemberAuthUtils.getLoginUserId()));
    }

    @Operation(summary = "企业团餐退款申请")
    @PostMapping("/company/order/refund/apply")
    public AjaxResult<FoodCompanyMealOrderResultVO> applyCompanyMealRefund(@RequestBody @Valid FoodMealOrderRefundApplyDTO dto) {
        return AjaxResult.okData(foodCompanyMealApplicationService.applyRefund(dto, MemberAuthUtils.getLoginUserId()));
    }

    @Operation(summary = "我的企业团餐订单")
    @PostMapping("/company/order/myList")
    public AjaxResult<PageRespVO<FoodCompanyMealOrderResultVO>> companyMealOrderList(@RequestBody(required = false) FoodMealOrderQueryVO query) {
        return AjaxResult.okData(foodCompanyMealApplicationService.findMyCompanyOrders(MemberAuthUtils.getLoginUserId(), query));
    }

    @Operation(summary = "企业团餐订单详情")
    @GetMapping("/company/order/detail/{orderId}")
    public AjaxResult<FoodCompanyMealOrderResultVO> companyMealOrderDetail(@PathVariable("orderId") String orderId) {
        FoodCompanyMealOrderResultVO vo = foodCompanyMealApplicationService.getCompanyOrderDetail(orderId, MemberAuthUtils.getLoginUserId());
        return vo == null ? AjaxResult.failMsg("订单不存在") : AjaxResult.okData(vo);
    }

    @Operation(summary = "企业团餐差额支付成功")
    @PostMapping("/company/order/paid")
    public AjaxResult<FoodCompanyMealOrderResultVO> companyMealOrderPaid(@RequestBody @Valid FoodCompanyMealOrderPaidDTO dto) {
        return AjaxResult.okData(foodCompanyMealApplicationService.handleCompanyMealOrderPaid(dto));
    }
}
