package shop.alien.dining.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiSort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import shop.alien.entity.result.R;
import shop.alien.dining.dto.ChangePhoneDto;
import shop.alien.dining.dto.UserProfileUpdateDto;
import shop.alien.dining.dto.VerifyTokenDto;
import shop.alien.dining.dto.WeChatLoginDto;
import shop.alien.dining.service.DiningUserService;
import shop.alien.dining.util.TokenUtil;
import shop.alien.dining.vo.DiningUserVo;
import shop.alien.dining.vo.TokenVerifyVo;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

/**
 * 点餐用户控制器
 *
 * @author ssk
 * @version 1.0
 * @date 2024/12/4
 */
@Slf4j
@Api(tags = {"微信点餐-登录（用户端）"})
@ApiSort(2)
@CrossOrigin
@RestController
@RequestMapping("/dining/user")
@RequiredArgsConstructor
public class DiningUserController {

    private final DiningUserService diningUserService;

    @ApiOperation(value = "微信小程序登录", notes = "标准流程：1. 前端调用 wx.login() 获取 code；2. 后端调用 jscode2session 获取 openid；3. 可选：通过 wx.getPhoneNumber() 获取 phoneCode 绑定手机号")
    @PostMapping("/wechatLogin")
    public R<DiningUserVo> wechatLogin(@Valid @RequestBody WeChatLoginDto loginDto, HttpServletRequest request) {
        log.info("微信小程序登录: code={}, phoneCode={}",
                loginDto.getCode(),
                loginDto.getPhoneCode());

        String macIp = getClientIp(request);
        DiningUserVo userVo = diningUserService.wechatLogin(
                loginDto.getCode(),
                loginDto.getPhoneCode(),
                macIp);
        if (userVo == null) {
            return R.fail("登录失败，请检查 code 是否有效（code 有效期5分钟，仅能使用一次）");
        }
        return R.data(userVo);
    }

    @ApiOperation(value = "获取用户信息", notes = "获取当前登录用户的详细信息（从token中获取用户ID）")
    @GetMapping("/getUserInfo")
    public R<DiningUserVo> getUserInfo() {
        // 从 token 获取当前用户ID
        Integer userId = TokenUtil.getCurrentUserId();
        if (userId == null) {
            return R.fail("用户未登录");
        }

        log.info("获取用户信息: userId={}", userId);

        DiningUserVo userVo = diningUserService.getUserInfo(userId.longValue());
        if (userVo == null) {
            return R.fail("用户不存在或状态异常");
        }
        return R.data(userVo);
    }

    @ApiOperation(value = "更新用户个人信息", notes = "登录后完善资料，支持更新昵称、头像、性别、生日等（从token中获取用户ID）")
    @PostMapping("/updateProfile")
    public R<DiningUserVo> updateProfile(@Valid @RequestBody UserProfileUpdateDto dto) {
        // 从 token 获取当前用户ID
        Integer userId = TokenUtil.getCurrentUserId();
        if (userId == null) {
            return R.fail("用户未登录");
        }

        // 使用 token 中的用户ID，忽略 DTO 中的 userId（防止用户修改其他用户的信息）
        dto.setUserId(userId.longValue());
        log.info("更新用户个人信息: userId={}", userId);

        DiningUserVo userVo = diningUserService.updateProfile(dto);
        if (userVo == null) {
            return R.fail("更新失败，用户不存在");
        }
        return R.data(userVo);
    }

    @ApiOperation(value = "更换手机号", notes = "校验验证码后更新 user_phone，成功后需重新登录（从token中获取用户ID）")
    @PostMapping("/changePhone")
    public R<DiningUserVo> changePhone(@Valid @RequestBody ChangePhoneDto dto) {
        // 从 token 获取当前用户ID
        Integer userId = TokenUtil.getCurrentUserId();
        if (userId == null) {
            return R.fail("用户未登录");
        }

        // 使用 token 中的用户ID，忽略 DTO 中的 userId（防止用户修改其他用户的手机号）
        dto.setUserId(userId.longValue());
        log.info("更换手机号: userId={}, newPhone={}", userId, dto.getNewPhone());

        DiningUserVo userVo = diningUserService.changePhone(dto);
        if (userVo == null) {
            return R.fail("更换失败，请检查验证码或用户状态");
        }
        return R.data(userVo);
    }

    @ApiOperation(value = "校验Token", notes = "验证Token是否有效，支持从请求头Authorization或请求体获取Token")
    @PostMapping("/verifyToken")
    public R<TokenVerifyVo> verifyToken(@RequestBody(required = false) VerifyTokenDto dto, HttpServletRequest request) {
        // 优先从请求体获取 token，如果没有则从请求头获取
        String token = null;
        if (dto != null && StringUtils.isNotBlank(dto.getToken())) {
            token = dto.getToken();
        } else {
            // 从请求头获取
            token = request.getHeader("Authorization");
        }

        if (StringUtils.isBlank(token)) {
            return R.fail("Token 不能为空，请通过请求头 Authorization 或请求体传递");
        }

        log.info("校验Token: token={}", token.length() > 20 ? token.substring(0, 20) + "****" : token);

        TokenVerifyVo verifyVo = diningUserService.verifyToken(token);
        if (verifyVo == null) {
            return R.fail("Token 校验失败");
        }

        if (verifyVo.getValid()) {
            return R.data(verifyVo);
        } else {
            return R.fail(verifyVo.getReason() != null ? verifyVo.getReason() : "Token 无效");
        }
    }

    @ApiOperation(value = "解析Token（调试用）", notes = "解析Token内容，用于调试，返回Token中的所有用户信息")
    @PostMapping("/parseToken")
    public R<com.alibaba.fastjson.JSONObject> parseToken(@RequestBody(required = false) VerifyTokenDto dto, HttpServletRequest request) {
        // 优先从请求体获取 token，如果没有则从请求头获取
        String token = null;
        if (dto != null && StringUtils.isNotBlank(dto.getToken())) {
            token = dto.getToken();
        } else {
            // 从请求头获取
            token = request.getHeader("Authorization");
        }

        if (StringUtils.isBlank(token)) {
            return R.fail("Token 不能为空");
        }

        log.info("DiningUserController.parseToken?token={}", token.length() > 20 ? token.substring(0, 20) + "****" : token);
        com.alibaba.fastjson.JSONObject userInfo = shop.alien.dining.util.TokenUtil.parseToken(token);
        if (userInfo == null) {
            return R.fail("Token 解析失败，请查看日志获取详细信息");
        }

        return R.data(userInfo);
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
