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

import static com.starry.common.constant.Constants.*;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.starry.admin.common.aspect.ClerkUserLogin;
import com.starry.admin.common.aspect.CustomUserLogin;
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
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.weichat.entity.WxUserLoginVo;
import com.starry.admin.modules.weichat.entity.WxUserQueryAddressVo;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.admin.modules.weichat.service.WxOauthService;
import com.starry.admin.modules.weichat.service.WxTokenService;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.redis.RedisCache;
import com.starry.common.result.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.util.Date;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

/**
 * 微信用户
 *
 * @author admin
 * @since 2019-03-25 15:39:39
 */
@Api(tags = "微信授权接口", description = "微信端用户授权相关接口")
@Slf4j
@RestController
@RequestMapping("/wx/oauth2")
public class WxOauthController {

    @Resource
    private WxCustomMpService wxCustomMpService;
    @Resource
    private IPlayCustomUserInfoService customUserInfoService;
    @Resource
    private IPlayClerkUserInfoService clerkUserInfoService;
    @Resource
    private WxTokenService tokenService;
    @Resource
    private WxOauthService wxOauthService;
    @Resource
    private RedisCache redisCache;

    @ApiOperation(value = "获取配置地址", notes = "获取微信JSAPI配置签名")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = WxJsapiSignature.class)})
    @PostMapping("/getConfigAddress")
    public R getConfigAddress(@ApiParam(value = "地址查询信息", required = true) @RequestBody WxUserQueryAddressVo vo)
            throws WxErrorException {
        // 默认回调地址
        String defaultAddress = "https://julyharbor.com/api/wx/oauth2/clerkLoginCallback";
        if (!StrUtil.isBlankIfStr(vo.getUrl())) {
            defaultAddress = vo.getUrl();
        }
        WxJsapiSignature wxJsapiSignature = wxCustomMpService.proxyWxMpService().createJsapiSignature(defaultAddress);
        return R.ok(wxJsapiSignature);
    }

    @ApiOperation(value = "获取店员登录地址", notes = "获取微信店员授权登录地址")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = String.class)})
    @PostMapping("/getClerkLoginAddress")
    public R getClerkLoginAddress(@ApiParam(value = "地址查询信息", required = true) @RequestBody WxUserQueryAddressVo vo) {
        // 默认回调地址
        String defaultAddress = "https://julyharbor.com/api/wx/oauth2/clerkLoginCallback";
        if (!StrUtil.isBlankIfStr(vo.getUrl())) {
            defaultAddress = vo.getUrl();
        }
        String url = wxCustomMpService.proxyWxMpService().getOAuth2Service().buildAuthorizationUrl(defaultAddress,
                "snsapi_userinfo", "STATE");
        return R.ok(url);
    }

    @ApiOperation(value = "店员登录回调", notes = "微信店员授权登录回调处理")
    @ApiImplicitParam(name = "code", value = "授权码", required = true, dataType = "String", paramType = "query")
    @GetMapping("/clerkLoginCallback")
    public void clerkLoginCallback(@RequestParam("code") String code) {
    }

    @ApiOperation(value = "店员登录", notes = "微信店员授权登录")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = JSONObject.class),
            @ApiResponse(code = 401, message = "登录失败"), @ApiResponse(code = 500, message = "用户不存在")})
    @PostMapping("/clerk/login")
    public R clerkLogin(@ApiParam(value = "登录信息", required = true) @Validated @RequestBody WxUserLoginVo vo) {
        try {
            String userId = wxOauthService.clerkUserLogin(vo.getCode());
            PlayClerkUserInfoEntity entity = clerkUserInfoService.selectById(userId);
            if (entity == null) {
                throw new CustomException("用户不存在");
            }
            clerkUserInfoService.ensureClerkSessionIsValid(entity);
            // 缓存租户信息
            String redisKey = "TENANT_INFO:" + entity.getId();
            redisCache.setCacheObject(redisKey, entity.getTenantId());
            String tokenValue = tokenService.createWxUserToken(entity.getId());
            JSONObject jsonObject = JSONObject.from(clerkUserInfoService.getVo(entity));
            jsonObject.put("tokenValue", TOKEN_PREFIX + tokenValue);
            jsonObject.put("tokenName", CLERK_USER_LOGIN_TOKEN);
            jsonObject.put("loginDate", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            jsonObject.put("pcData", clerkUserInfoService.getPcData(entity));
            clerkUserInfoService.updateTokenById(entity.getId(), tokenValue);
            return R.ok(jsonObject);
        } catch (CustomException e) {
            log.warn("店员登录失败，code={}, message={}", vo.getCode(), e.getMessage());
            return R.unauthorized().message(e.getMessage());
        } catch (Exception e) {
            log.error("顾客登录失败，", e);
            return R.unauthorized();
        }
    }

    @ApiOperation(value = "店员开发登录", notes = "微信店员开发环境登录（固定用户）")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = JSONObject.class),
            @ApiResponse(code = 401, message = "登录失败"), @ApiResponse(code = 500, message = "用户不存在")})
    @PostMapping("/clerk/login/dev")
    public R clerkLoginDev(@ApiParam(value = "登录信息", required = true) @Validated @RequestBody WxUserLoginVo vo) {
        try {
            String userId = "a4471ef596a1";
            PlayClerkUserInfoEntity entity = clerkUserInfoService.selectById(userId);
            if (entity == null) {
                throw new CustomException("用户不存在");
            }
            clerkUserInfoService.ensureClerkSessionIsValid(entity);
            // 缓存租户信息
            String redisKey = "TENANT_INFO:" + entity.getId();
            redisCache.setCacheObject(redisKey, entity.getTenantId());
            String tokenValue = tokenService.createWxUserToken(entity.getId());
            JSONObject jsonObject = JSONObject.from(clerkUserInfoService.getVo(entity));
            jsonObject.put("tokenValue", TOKEN_PREFIX + tokenValue);
            jsonObject.put("tokenName", CLERK_USER_LOGIN_TOKEN);
            jsonObject.put("loginDate", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            jsonObject.put("pcData", clerkUserInfoService.getPcData(entity));
            clerkUserInfoService.updateTokenById(entity.getId(), tokenValue);
            return R.ok(jsonObject);
        } catch (CustomException e) {
            log.warn("店员开发登录失败，message={}", e.getMessage());
            return R.unauthorized().message(e.getMessage());
        } catch (Exception e) {
            log.error("顾客登录失败，", e);
            return R.unauthorized();
        }
    }

    @ApiOperation(value = "店员ID登录", notes = "根据店员ID直接登录")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = JSONObject.class),
            @ApiResponse(code = 500, message = "用户不存在")})
    @PostMapping("/clerk/loginById")
    public R loginById(@ApiParam(value = "登录信息", required = true) @Validated @RequestBody WxUserLoginVo vo) {
        PlayClerkUserInfoEntity entity = clerkUserInfoService.selectById(vo.getCode());
        if (entity == null) {
            throw new CustomException("用户不存在");
        }
        clerkUserInfoService.ensureClerkIsActive(entity);
        // 缓存租户信息
        String redisKey = "TENANT_INFO:" + entity.getId();
        redisCache.setCacheObject(redisKey, entity.getTenantId());
        JSONObject jsonObject = JSONObject.from(clerkUserInfoService.getVo(entity));
        String tokenValue = tokenService.createWxUserToken(entity.getId());
        jsonObject.put("tokenValue", TOKEN_PREFIX + tokenValue);
        jsonObject.put("tokenName", CLERK_USER_LOGIN_TOKEN);
        jsonObject.put("loginDate", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        clerkUserInfoService.updateTokenById(entity.getId(), tokenValue);
        return R.ok(jsonObject);
    }

    @ApiOperation(value = "店员登出", notes = "店员退出登录")
    @ApiResponses({@ApiResponse(code = 200, message = "登出成功")})
    @ClerkUserLogin
    @GetMapping("/clerk/logout")
    public R clerkLogout() {
        wxOauthService.clerkUserLogout(ThreadLocalRequestDetail.getClerkUserInfo());
        return R.ok("登出成功");
    }

    @ApiOperation(value = "获取顾客登录地址", notes = "获取微信顾客授权登录地址")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = String.class)})
    @PostMapping("/getCustomLoginAddress")
    public R getCustomLoginAddress(@ApiParam(value = "地址查询信息", required = true) @RequestBody WxUserQueryAddressVo vo) {
        // 默认回调地址
        String defaultAddress = "https://julyharbor.com/api/wx/oauth2/customLoginCallback";
        if (!StrUtil.isBlankIfStr(vo.getUrl())) {
            defaultAddress = vo.getUrl();
        }
        String url = wxCustomMpService.proxyWxMpService().getOAuth2Service().buildAuthorizationUrl(defaultAddress,
                "snsapi_userinfo", "STATE");
        return R.ok(url);
    }

    @GetMapping("/checkSubscribe")
    @CustomUserLogin
    public R checkSubscribe() {
        String openid = ThreadLocalRequestDetail.getCustomUserInfo().getOpenid();
        String tenantId = SecurityUtils.getTenantId();
        return R.ok(wxCustomMpService.checkSubscribe(openid, tenantId));
    }

    @ApiOperation(value = "顾客登录回调", notes = "微信顾客授权登录回调处理")
    @ApiImplicitParam(name = "code", value = "授权码", required = true, dataType = "String", paramType = "query")
    @GetMapping("/customLoginCallback")
    public void customLoginCallback(@RequestParam("code") String code) {

    }

    @ApiOperation(value = "顾客登录", notes = "微信顾客授权登录")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = JSONObject.class),
            @ApiResponse(code = 401, message = "登录失败"), @ApiResponse(code = 500, message = "用户不存在")})
    @PostMapping("/custom/login")
    public R customLogin(@ApiParam(value = "登录信息", required = true) @Validated @RequestBody WxUserLoginVo vo) {
        log.info("顾客登录接口调用，code = {}", vo.getCode());
        try {
            String userId = wxOauthService.customUserLogin(vo.getCode());
            PlayCustomUserInfoEntity entity = customUserInfoService.selectById(userId);
            if (entity == null) {
                throw new CustomException("用户不存在");
            }
            // 缓存租户信息
            String redisKey = "TENANT_INFO:" + entity.getId();
            redisCache.setCacheObject(redisKey, entity.getTenantId());
            JSONObject jsonObject = JSONObject.from(entity);
            String tokenValue = tokenService.createWxUserToken(entity.getId());
            jsonObject.put("tokenValue", TOKEN_PREFIX + tokenValue);
            jsonObject.put("tokenName", CUSTOM_USER_LOGIN_TOKEN);
            jsonObject.put("loginDate", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            customUserInfoService.updateTokenById(entity.getId(), tokenValue);
            return R.ok(jsonObject);
        } catch (Exception e) {
            log.error("登录失败", e);
            return R.unauthorized();
        }
    }

    @ApiOperation(value = "顾客ID登录", notes = "根据顾客ID直接登录")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = JSONObject.class),
            @ApiResponse(code = 500, message = "用户不存在")})
    @PostMapping("/custom/loginById")
    public R loginById1(@ApiParam(value = "登录信息", required = true) @Validated @RequestBody WxUserLoginVo vo) {
        PlayCustomUserInfoEntity entity = customUserInfoService.selectById(vo.getCode());
        if (entity == null) {
            throw new CustomException("用户不存在");
        }
        // 缓存租户信息
        String redisKey = "TENANT_INFO:" + entity.getId();
        redisCache.setCacheObject(redisKey, entity.getTenantId());
        JSONObject jsonObject = JSONObject.from(entity);
        String tokenValue = tokenService.createWxUserToken(entity.getId());
        jsonObject.put("tokenValue", TOKEN_PREFIX + tokenValue);
        jsonObject.put("tokenName", CLERK_USER_LOGIN_TOKEN);
        jsonObject.put("loginDate", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        customUserInfoService.updateTokenById(entity.getId(), tokenValue);
        return R.ok(jsonObject);
    }

    @ApiOperation(value = "顾客登出", notes = "顾客退出登录")
    @ApiResponses({@ApiResponse(code = 200, message = "登出成功")})
    @GetMapping("/custom/logout")
    @CustomUserLogin
    public R customLogout() {
        wxOauthService.customUserLogout(ThreadLocalRequestDetail.getCustomUserInfo());
        return R.ok("登出成功");
    }

}
