package com.liquidnet.service.adam.controller;

import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.commons.lang.constant.LnsRegex;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.adam.dto.AdamThirdPartParam;
import com.liquidnet.service.adam.dto.AdamUserInfoParam;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.service.*;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import java.util.*;
import java.util.stream.Collectors;

@ApiSupport(order = 10020)
@Api(tags = "用户中心")
@Slf4j
@Validated
@RestController
@RequestMapping("user")
public class AdamUserController {
    @Autowired
    Environment environment;
    @Autowired
    IAdamUserService adamUserService;
    @Autowired
    IAdamUserInfoService adamUserInfoService;
    @Autowired
    IAdamThirdPartyService adamThirdPartyService;
    @Autowired
    IAdamRealNameService adamRealNameService;
    @Autowired
    IAdamRdmService adamRdmService;

    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "个人资料编辑")
    @PostMapping(value = {"edit"})
    public ResponseDto<AdamUserInfoVo> edit(@Valid @RequestBody AdamUserInfoParam parameter) {
        log.debug("parameter:{}", JsonUtils.toJson(parameter));

        final AdamTagVo sex = parameter.getSex();
        if (null == sex) {
            return ResponseDto.failure(ErrorMapping.get("10011"));
        }
        List<AdamTagVo> sexList = this.getTagsForSex().getData();
        List<AdamTagVo> existSexVoList = sexList.stream().filter(r -> r.getVal().equals(sex.getVal())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(existSexVoList)) {
            return ResponseDto.failure(ErrorMapping.get("10011"));
        }
        List<AdamTagParentVo> tagMe = parameter.getTagMe();
        if (null == tagMe) {
            return ResponseDto.failure(ErrorMapping.get("10012"));
        }
        List<String> chooseTagMeParentValList = tagMe.stream().map(AdamTagParentVo::getVal).collect(Collectors.toList());
        List<AdamTagParentVo> existParentVoList = this.getTagsForMusic().getData()
                .stream().filter(r -> chooseTagMeParentValList.contains(r.getVal())).collect(Collectors.toList());
        if (chooseTagMeParentValList.size() != existParentVoList.size()) {
            return ResponseDto.failure(ErrorMapping.get("10012"));
        }
        for (AdamTagParentVo parentVo : existParentVoList) {
            List<AdamTagVo> tagVos = parentVo.getTagVos();

            List<AdamTagVo> chooseTagVos =
                    tagMe.stream().filter(r -> r.getVal().equals(parentVo.getVal())).collect(Collectors.toList()).get(0).getTagVos();

            if (tagVos.size() < chooseTagVos.size()) {
                return ResponseDto.failure(ErrorMapping.get("10012"));
            }

            List<String> chooseTagVoVals = chooseTagVos.stream().map(AdamTagVo::getVal).collect(Collectors.toList());

            List<AdamTagVo> existTagVos = tagVos.stream().filter(r -> chooseTagVoVals.contains(r.getVal())).collect(Collectors.toList());
            if (chooseTagVoVals.size() != existTagVos.size()) {
                return ResponseDto.failure(ErrorMapping.get("10012"));
            }
        }

        AdamUserInfoVo editUserInfoVo = adamRdmService.getUserInfoVoByUid(CurrentUtil.getCurrentUid());
        editUserInfoVo.setAvatar(parameter.getAvatar());
        editUserInfoVo.setBackground(parameter.getBackground());
        editUserInfoVo.setNickname(parameter.getNickname());
        editUserInfoVo.setSex(existSexVoList.get(0));
        editUserInfoVo.setBirthday(parameter.getBirthday());
        editUserInfoVo.setArea(parameter.getArea());
        editUserInfoVo.setSignature(parameter.getSignature());
        editUserInfoVo.setTagMe(tagMe);
        editUserInfoVo.setIsComplete(StringUtils.isEmpty(editUserInfoVo.getUpdatedAt()) ? 0 : 1);
        editUserInfoVo.setUpdatedAt(DateUtil.getNowTime());

        adamUserInfoService.edit(editUserInfoVo);

        return ResponseDto.success(editUserInfoVo);
    }

    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "音乐风格")
    @GetMapping(value = {"tag/ms"})
    public ResponseDto<List<AdamTagParentVo>> getTagsForMusic() {
        List<AdamTagParentVo> tagsForMusic = adamRdmService.getTagsForMusic();
        if (CollectionUtils.isEmpty(tagsForMusic)) {
            tagsForMusic = new ArrayList<>();
            tagsForMusic.add(AdamTagParentVo.getNew().setVal("MMS01").setDesc("民歌").setTagVos(Arrays.asList(
                    AdamTagVo.getNew().setVal("MMS0101").setDesc("A"),
                    AdamTagVo.getNew().setVal("MMS0102").setDesc("B")
            )));
            tagsForMusic.add(AdamTagParentVo.getNew().setVal("MMS02").setDesc("house").setTagVos(Arrays.asList(
                    AdamTagVo.getNew().setVal("MMS0201").setDesc("C"),
                    AdamTagVo.getNew().setVal("MMS0202").setDesc("D")
            )));
            tagsForMusic.add(AdamTagParentVo.getNew().setVal("MMS03").setDesc("R&B").setTagVos(Arrays.asList(
                    AdamTagVo.getNew().setVal("MMS0301").setDesc("E"),
                    AdamTagVo.getNew().setVal("MMS0302").setDesc("F")
            )));

            adamRdmService.setTagsForMusic(tagsForMusic);
        }
        return ResponseDto.success(tagsForMusic);
    }

    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "性别列表")
    @GetMapping(value = {"tag/sex"})
    public ResponseDto<List<AdamTagVo>> getTagsForSex() {
        List<AdamTagVo> tagsForSex = adamRdmService.getTagsForSex();
        if (CollectionUtils.isEmpty(tagsForSex)) {
            tagsForSex = new ArrayList<>();
            tagsForSex.add(AdamTagVo.getNew().setVal("MS00").setDesc("其他性别"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS01").setDesc("男性"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS02").setDesc("女性"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS03").setDesc("跨性别女性"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS04").setDesc("跨性别男性"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS05").setDesc("双性别者"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS06").setDesc("性别模糊"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS07").setDesc("性别流动"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS08").setDesc("无性别者"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS09").setDesc("不确定性别"));
            tagsForSex.add(AdamTagVo.getNew().setVal("MS10").setDesc("不在乎性别"));

            adamRdmService.setTagsForSex(tagsForSex);
        }
        return ResponseDto.success(tagsForSex);
    }

    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "手机号修改")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "mobile", value = "新手机号"),
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "code", value = "验证码"),
    })
    @PostMapping(value = {"edit/mobile"})
    public ResponseDto<Object> editMobile(@Pattern(regexp = "\\d{11}", message = "手机号格式有误")
                                          @RequestParam String mobile,
                                          @Pattern(regexp = "\\d{6}", message = "验证码格式有误")
                                          @RequestParam String code) {
        log.debug("mobile:{},code:{}", mobile, code);

        if (!this.checkSmsCode(mobile, code)) return ResponseDto.failure(ErrorMapping.get("10002"));

        String uid = CurrentUtil.getCurrentUid();

        adamUserInfoService.editMobile(uid, mobile);

        return ResponseDto.success();
    }

    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "实名认证")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "name", value = "姓名"),
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "idCard", value = "证件号"),
    })
    @PostMapping(value = {"identity"})
    public ResponseDto<AdamRealInfoVo> identity(@Pattern(regexp = LnsRegex.Valid.CHINESE_HANZI, message = "姓名必须为2～20位汉字")
                                                @RequestParam String name,
                                                @Pattern(regexp = LnsRegex.Valid.CHINESE_ID_CARD, message = "身份证号格式有误")
                                                @RequestParam String idCard) {
        log.debug("name:{},idCard:{}", name, idCard);

        AdamRealInfoVo infoVo = adamUserService.identity(CurrentUtil.getCurrentUid(), name, idCard);

        return ResponseDto.success(infoVo);
    }

//    @ApiOperationSupport(order = 6)
//    @ApiOperation(value = "密码修改")
//    @ApiImplicitParams({
//            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "mobile", value = "手机号"),
//            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "password", value = "密码"),
//            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "code", value = "验证码"),
//    })
//    @PostMapping(value = {"edit/pwd"})
//    public ResponseDto<Object> editPwd(@RequestParam String mobile, @RequestParam String password, @RequestParam String code) {
//        log.info("mobile:{},password:{},code:{}", mobile, password, code);
//
//
//
//        return ResponseDto.success();
//    }

    @ApiOperationSupport(order = 7)
    @ApiOperation(value = "绑定第三方账号")
    @PostMapping(value = {"tpa/bind"})
    public ResponseDto<List<AdamThirdPartInfoVo>> bindTpa(@RequestBody @Valid AdamThirdPartParam parameter) {
        log.debug("login by tpa:{}", JsonUtils.toJson(parameter));

        if (StringUtils.isBlank(parameter.getOpenId())) {
            return ResponseDto.failure(ErrorMapping.get("10009"));
        }

        String currentUid = CurrentUtil.getCurrentUid();

        String existUid = adamRdmService.getUidByPlatformOpenId(parameter.getPlatform(), parameter.getOpenId());
        if (StringUtils.isNotEmpty(existUid)) {
            if (existUid.equals(currentUid)) {
                return ResponseDto.success(adamRdmService.getThirdPartVoListByUid(currentUid));
            }
            Boolean force = parameter.getForce();
            if (null != force && force) {// 强制解绑，并重新绑定当前账号
                adamUserService.bindTpaForce(currentUid, existUid, parameter);
                return ResponseDto.success();
            }
            return ResponseDto.failure(ErrorMapping.get("10007"));
        }

        // 判断该用户是否已绑定同类型第三方账号
        AdamThirdPartInfoVo bindThirdPartVo = adamRdmService.getThirdPartVoByUidPlatform(currentUid, parameter.getPlatform());
        if (null != bindThirdPartVo) return ResponseDto.failure(ErrorMapping.get("10008"));

        adamUserService.bindTpa(currentUid, parameter);

        return ResponseDto.success(adamRdmService.getThirdPartVoListByUid(currentUid));
    }

    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "解绑第三方账号")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "platform", value = "平台类型", allowableValues = "WEIBO,WECHAT,QQ"),
    })
    @PostMapping(value = {"tpa/unbind/{platform}"})
    public ResponseDto<List<AdamThirdPartInfoVo>> unbindTpa(//@Pattern(regexp = "\\b(WEIBO,WECHAT,QQ)\\b", message = "平台类型无效")
                                                            @PathVariable String platform) {
        String currentUid = CurrentUtil.getCurrentUid();
        log.debug("unbind tpa.platform:{},uid:{}", platform, currentUid);

        adamUserService.unBindTpa(currentUid, platform);

        return ResponseDto.success(adamRdmService.getThirdPartVoListByUid(currentUid));
    }

    @ApiOperationSupport(order = 9)
    @ApiOperation(value = "个人信息")
    @PostMapping(value = {"info"})
    public ResponseDto<Map<String, Object>> info() {
        String currentUid = CurrentUtil.getCurrentUid();

        Map<String, Object> map = new HashMap<>();
        map.put("userInfo", adamRdmService.getUserInfoVoByUid(currentUid));
        map.put("realNameInfo", adamRdmService.getRealInfoVoByUid(currentUid));
        map.put("thirdPartInfo", adamRdmService.getThirdPartVoListByUid(currentUid));
        map.put("userMemberVo", adamRdmService.getUserMemberVoByUid(currentUid));

        return ResponseDto.success(map);
    }

    /* ---------------------------- Internal Method ---------------------------- */

    private boolean checkSmsCode(String mobile, String code) {
        if (Arrays.asList("dev", "test").contains(environment.getProperty("spring.profiles.active"))) {
            return "111111".equals(code);
        }

        LinkedMultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
        paramsMap.add("mobile", mobile);
        paramsMap.add("code", code);
        LinkedMultiValueMap<String, String> headersMap = new LinkedMultiValueMap<>();
        headersMap.add("token", null);

        try {
            // TODO: 2021/5/12
            String respStr = HttpUtil.get("https://service.zhengzai.tv/smsValidation", paramsMap, headersMap);
            log.info("###PHP.API.RESP:{}", respStr);

            Map respMap = JsonUtils.fromJson(respStr, Map.class);

            return StringUtils.equalsIgnoreCase("OK", (String) respMap.get("message"));
        } catch (Exception e) {
            log.error("验证码验证异常[mobile:{},code:{}]", mobile, code, e);
            return false;
        }
    }
}
