package com.fs.app.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.fs.app.annotation.Login;
import com.fs.app.dto.*;
import com.fs.app.service.AppDoctorService;
import com.fs.app.vo.BankInfoVo;
import com.fs.audit.dto.CreateAuditRequestDTO;
import com.fs.audit.enums.AuditStatus;
import com.fs.audit.enums.AuditTypeEnum;
import com.fs.audit.enums.CreditStatus;
import com.fs.audit.service.AuditService;
import com.fs.common.BeanCopyUtils;
import com.fs.common.core.domain.AjaxResult;
import com.fs.common.core.domain.R;
import com.fs.common.core.domain.SmsResponse;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.exception.CustomException;
import com.fs.common.exception.ServiceException;
import com.fs.common.utils.SmsVerifyCodeUtils;
import com.fs.common.utils.sign.Md5Utils;
import com.fs.company.domain.CompanyUser;
import com.fs.company.service.ICompanyUserService;
import com.fs.company.service.impl.CompanyUserServiceImpl;
import com.fs.doctor.domain.*;
import com.fs.doctor.enums.DoctorAuditStatus;
import com.fs.doctor.enums.WithdrawPaymentStatusEnum;
import com.fs.doctor.mapper.LimitCycleMapper;
import com.fs.doctor.service.IDoctorBalanceLogService;
import com.fs.doctor.service.IDoctorCompanyUserService;
import com.fs.doctor.service.IDoctorService;
import com.fs.doctor.service.IWithdrawRecordsService;
import com.fs.doctor.vo.DoctorCreditVO;
import com.fs.doctor.vo.DoctorVO;
import com.fs.provider.enums.BalanceChangeTypeEnum;
import com.fs.system.service.ISysDictDataService;
import com.fs.system.vo.CommonCompanyInfo;
import com.fs.system.vo.DictVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.fs.audit.enums.AuditStatus.PENDING;


@Api("app登录接口")
@RestController
@RequestMapping(value = "/app/dockerInfo")
public class AppUserController extends AppBaseController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IDoctorService doctorService;

    @Autowired
    private IWithdrawRecordsService iWithdrawRecordsService;
    @Autowired
    private IDoctorBalanceLogService iDoctorBalanceLogService;
    @Autowired
    private IDoctorCompanyUserService iDoctorCompanyUserService;
    @Autowired
    private AuditService auditService;
    @Autowired
    private AppDoctorService appDoctorService;
    @Autowired
    private SmsVerifyCodeUtils smsVerifyCodeUtils;
    @Autowired
    private ICompanyUserService companyUserService;

    @ApiOperation("查询医生详情")
    @GetMapping("/getDockerInfo")
    public AjaxResult getDockerInfo() {
        String userId = getUserId();
        // 查询积分余额
        Doctor doctor = doctorService.selectDoctorById(Long.parseLong(userId));
        return AjaxResult.success(doctor);
    }

    /**
     * 发送短信登录验证码
     *
     * @param phone 手机号
     * @return 发送结果
     */
    @ApiOperation("发送短信验证码")
    @PostMapping("/sendSmsCode")
    public R sendSmsCode(@RequestParam String phone) {

        if (phone == null || !phone.matches("^1[3-9]\\d{9}$")) {
            return R.error("手机号格式不正确");
        }

        String redisKey = "sms:login:" + phone;
        String existCode = redisCache.getCacheObject(redisKey);
        if (existCode != null) {
            Long expireTime = redisCache.getExpire(redisKey);
            if (expireTime != null && expireTime > 240) {
                return R.error("验证码已发送，请稍后再试");
            }
        }

        SmsResponse response = smsVerifyCodeUtils.sendVerifyCode(phone);
        if (!response.isSuccess()) {
            return R.ok("验证码发送出错");
        }

        redisCache.setCacheObject(redisKey, response.getCode(), 300, TimeUnit.SECONDS);


        return R.ok("验证码发送成功");
    }

    @ApiOperation("登录")
    @PostMapping("/login")
    @Transactional
    public AjaxResult login(@Validated @RequestBody FsUserLoginDto fsUserLoginDto) {
        int loginType = fsUserLoginDto.getLoginType();
        Doctor doctor = doctorService.selectDoctorByPhone(fsUserLoginDto.getPhone());
        switch (loginType) {
            case 1:
                return loginByPassword(fsUserLoginDto,doctor);
            case 2:
                return loginBySmsCode(fsUserLoginDto,doctor);
            default:
                return AjaxResult.error("请选择正确的登陆类型！");
        }
    }

    private AjaxResult loginByPassword(FsUserLoginDto fsUserLoginDto,Doctor doctor) {
        if (StringUtils.isEmpty(fsUserLoginDto.getPhone()) || StringUtils.isEmpty(fsUserLoginDto.getPassword())) {
            return AjaxResult.error("账号或密码不能为空");
        }
        if (ObjectUtils.isEmpty(doctor)) {
            return AjaxResult.error("账号不存在，请先注册账号");
        }


            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            if (!encoder.matches(fsUserLoginDto.getPassword(), doctor.getPwd())) {
                return AjaxResult.error("密码不正确");
            }
            return generateTokenAndReturn(doctor);

    }

    public AjaxResult loginBySmsCode(FsUserLoginDto fsUserLoginDto, Doctor doctor) {
        String redisKey = "sms:login:" + fsUserLoginDto.getPhone();
        String cacheCode = redisCache.getCacheObject(redisKey);

        if (cacheCode == null) {
            return AjaxResult.error("验证码已过期，请重新获取");
        }

        if (!cacheCode.equals(fsUserLoginDto.getCode())) {
            return AjaxResult.error("验证码不正确");
        }

        logger.info("短信验证码登录 - 手机号: {}, companyId: {}, companyUserId: {}, 用户是否存在: {}",
                fsUserLoginDto.getPhone(), fsUserLoginDto.getCompanyId(),
                fsUserLoginDto.getCompanyUserId(), doctor != null);

        AjaxResult ajaxResult;

        // 判断是否通过业务员扫码进入（有业务员ID和公司ID）
        boolean hasCompanyInfo = fsUserLoginDto.getCompanyId() != null && fsUserLoginDto.getCompanyUserId() != null;

        if (doctor == null) {
            // 用户未注册
            if (!hasCompanyInfo) {
                return AjaxResult.error("未注册用户需要通过业务员邀请扫码注册");
            }

            // 自动注册新用户
            Doctor newDoctor = new Doctor();
            newDoctor.setMobile(fsUserLoginDto.getPhone());
            newDoctor.setDoctorId("CD" + IdUtil.getSnowflake(1L, 1L).nextId());
            newDoctor.setCompanyId(fsUserLoginDto.getCompanyId());
            newDoctor.setStatus(DoctorAuditStatus.VOID.getCode().longValue());
            doctorService.insertDoctor(newDoctor);

            logger.info("自动注册新用户 - doctorId: {}, companyId: {}", newDoctor.getDoctorId(), newDoctor.getCompanyId());

            // 绑定业务员关系
            bindCompanyUser(newDoctor, fsUserLoginDto.getCompanyUserId(), fsUserLoginDto.getCompanyId());

            ajaxResult = generateTokenAndReturn(newDoctor);

        } else {
            // 用户已注册，直接登录
            logger.info("用户已注册 - doctorId: {}", doctor.getDoctorId());

            // 如果有业务员信息，检查是否需要建立新的绑定关系
            if (hasCompanyInfo) {
                Long companyUserId = Long.parseLong(fsUserLoginDto.getCompanyUserId());
                // 检查是否已经绑定过该业务员
                DoctorCompanyUser existBinding = iDoctorCompanyUserService.selectByDoctorIdAndCompanyUserId(
                        doctor.getDoctorId(), String.valueOf(companyUserId));

                if (existBinding == null) {
                    // 未绑定，建立新的绑定关系
                    logger.info("建立新的业务员绑定 - doctorId: {}, companyUserId: {}", doctor.getDoctorId(), companyUserId);
                    bindCompanyUser(doctor, fsUserLoginDto.getCompanyUserId(), fsUserLoginDto.getCompanyId());
                } else {
                    logger.info("业务员已绑定 - doctorId: {}, companyUserId: {}", doctor.getDoctorId(), companyUserId);
                }
            }

            ajaxResult = generateTokenAndReturn(doctor);
        }

        redisCache.deleteObject(redisKey);

        return ajaxResult;
    }

    /**
     * 绑定业务员关系
     */
    private void bindCompanyUser(Doctor doctor, String companyUserIdStr, Long companyId) {
        if (StringUtils.isEmpty(companyUserIdStr)) {
            throw new CustomException("扫码未获取到业务员ID");
        }

        Long companyUserId = Long.parseLong(companyUserIdStr);
        CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserId);
        if (companyUser == null) {
            throw new CustomException("业务员信息不存在");
        }

        DoctorCompanyUser doctorCompanyUser = new DoctorCompanyUser();
        doctorCompanyUser.setCompanyId(companyId);
        doctorCompanyUser.setCompanyUserId(companyUserIdStr);
        doctorCompanyUser.setCompanyUserName(companyUser.getNickName());
        doctorCompanyUser.setDoctorId(doctor.getDoctorId());
        doctorCompanyUser.setProduct(companyUser.getProductCode());
        doctorCompanyUser.setBindtime(new Date());
        iDoctorCompanyUserService.insertDoctorCompanyUser(doctorCompanyUser);

        logger.info("业务员绑定成功 - doctorId: {}, companyUserId: {}, companyUserName: {}",
                doctor.getDoctorId(), companyUserId, companyUser.getNickName());
    }

    private AjaxResult generateTokenAndReturn(Doctor doctor) {
        String token = jwtUtils.generateToken(doctor.getId());
        redisCache.setCacheObject("userToken:" + doctor.getId(), token, 604800, TimeUnit.SECONDS);
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("docker", doctor);

        return AjaxResult.success("", map);
    }

    @Login
    @ApiOperation("修改密码")
    @PostMapping("/resetPassword")
    public AjaxResult resetPassword(@RequestBody PasswordDto passwordDto) {
        String phone = passwordDto.getPhone();
        String code = passwordDto.getCode();
        String newPassword = passwordDto.getNewPassword();
        String confirmPassword = passwordDto.getNewPassword();


        String redisKey = "sms:login:" + passwordDto.getPhone();
        String cacheCode = redisCache.getCacheObject(redisKey);

        if (cacheCode == null) {
            return AjaxResult.error("验证码已过期，请重新获取");
        }

        if (!cacheCode.equals(code)) {
            return AjaxResult.error("验证码不正确");
        }

        if (!newPassword.equals(confirmPassword)) {
            return AjaxResult.error("两次输入密码不一致，请检查");
        }
        Doctor doctor = new Doctor();
        doctor.setMobile(phone);
        List<Doctor> doctors = doctorService.selectDoctorList(doctor);
        if (CollectionUtil.isEmpty(doctors)) {
            return AjaxResult.error("此电话号码未绑定用户");
        }
        if (doctors.size() > 1) {
            return AjaxResult.error("此电话号码绑定多个账号，请联系管理员");
        }
        doctor = doctors.get(0);
//        String redisCode = redisCache.getCacheObject("sms:code:" + phone);
//        if (StringUtils.isEmpty(redisCode)){
//            return AjaxResult.error("验证码已过期，请重新发送");
//        }
//        if (!redisCode.equals(code)) {
//            return AjaxResult.error("验证码错误");
//        }
        //新密码加密
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        doctor.setPwd(encoder.encode(newPassword));
        doctorService.resetPassword(doctor);

        redisCache.deleteObject(redisKey);
        return AjaxResult.success("密码修改成功");
    }

    @Login
    @ApiOperation("获取积分信息")
    @PostMapping("/getCreditInfo")
    public AjaxResult getCreditInfo() {
        String userId = getUserId();
        // 查询积分余额
        Doctor doctor = doctorService.selectDoctorById(Long.parseLong(userId));
        WithdrawRecords withdrawRecords = new WithdrawRecords();
        withdrawRecords.setDoctorId(doctor.getId());
        withdrawRecords.setAuditStatus(Long.parseLong(CreditStatus.PENDING.getCode().toString()));
        List<WithdrawRecords> withdrawRecordsList = iWithdrawRecordsService.selectWithdrawRecordsList(withdrawRecords);
        // 计算用户总得支出待入账
        BigDecimal waitCreditNum = withdrawRecordsList.stream()
                .map(WithdrawRecords::getCashAmount) // 提取 cash_amount 字段
                .filter(Objects::nonNull)            // 过滤掉 null 值（可选，但推荐）
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        DoctorCreditVO doctorCreditVO = new DoctorCreditVO();
        doctorCreditVO.setCreditNum(doctor.getBalance());
        doctorCreditVO.setWaitCreditNum(waitCreditNum);
        return AjaxResult.success(doctorCreditVO);
    }



    @Login
    @ApiOperation("获取积分流转明细")
    @PostMapping("/getCreditHistoryData")
    public AjaxResult getCreditHistoryData(@RequestBody GetCreditDto getCreditDto) {
        String userId = getUserId();

        DoctorBalanceLog doctorBalanceLog = new DoctorBalanceLog();
        doctorBalanceLog.setDoctorId(Long.parseLong(userId));
        doctorBalanceLog.setChangeType(getCreditDto.getChangeType());
        doctorBalanceLog.setBusinessType(getCreditDto.getBusinessType());
        List<DoctorBalanceLog> doctorBalanceLogs = iDoctorBalanceLogService.selectDoctorBalanceLogList(doctorBalanceLog);

        // 如果传入了selectTime，进行时间筛选
        if (getCreditDto.getSelectTime() != null && !getCreditDto.getSelectTime().isEmpty()) {
            String targetMonth = getCreditDto.getSelectTime(); // 格式: "2026-03"

            doctorBalanceLogs = doctorBalanceLogs.stream()
                    .filter(log -> {
                        if (log.getCreateAt() == null) {
                            return false;
                        }
                        // 假设createTime是Date类型，将其格式化为"yyyy-MM"进行比较
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
                        String logMonth = sdf.format(log.getCreateAt());
                        return targetMonth.equals(logMonth);
                    })
                    .collect(Collectors.toList());
        }

        BigDecimal totalChangeMoney = doctorBalanceLogs.stream()
                .map(DoctorBalanceLog::getChangeMoney)
                .filter(Objects::nonNull) // 避免 NullPointerException
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        Map<String, Object> result = new HashMap<>();
        result.put("doctorBalanceLogs", doctorBalanceLogs);
        result.put("total", totalChangeMoney);
        return AjaxResult.success(result);
    }

    @Login
    @ApiOperation("医生发起用户提现")
    @PostMapping("/launchCreditWithdraw")
    @Transactional
    public AjaxResult launchCreditWithdraw(@RequestBody CreditWithdrawDto creditWithdrawDto) {
        BigDecimal amount = null;
        if (creditWithdrawDto.getCreditAmount() != null){
            amount = creditWithdrawDto.getCreditAmount();
        }
        long userId = Long.parseLong(getUserId());
        // 需要扣除医生积分账户余额
        Doctor doctor = doctorService.selectDoctorById(userId);
        CommonCompanyInfo commonCompanyInfo = getCompanyIdAndCompanyUserId(doctor.getDoctorId());

        WithdrawRecords withdrawRecords = new WithdrawRecords();
        withdrawRecords.setDoctorId(userId);
        withdrawRecords.setDoctorName(doctor.getDoctorName());
        withdrawRecords.setPointsAmount(amount);
        withdrawRecords.setCashAmount(amount);
        withdrawRecords.setApplyTime(new Date());
        withdrawRecords.setAuditStatus(Long.parseLong(CreditStatus.PENDING.getCode().toString()));
        withdrawRecords.setPaymentStatus(Long.parseLong(WithdrawPaymentStatusEnum.WAIT_PAY.getCode().toString()));
        withdrawRecords.setCompanyId(commonCompanyInfo.getCompanyId());
        iWithdrawRecordsService.insertWithdrawRecords(withdrawRecords);


        CreateAuditRequestDTO createAuditRequestDTO = new CreateAuditRequestDTO();
        createAuditRequestDTO.setBusinessId(withdrawRecords.getWithdrawId());
        createAuditRequestDTO.setBusinessType(AuditTypeEnum.WITHDRAW_AUDIT.getCode());
        createAuditRequestDTO.setTempCode(AuditTypeEnum.WITHDRAW_AUDIT.getCode());
        createAuditRequestDTO.setInitiatorId(userId);
        createAuditRequestDTO.setCompanyId(commonCompanyInfo.getCompanyId());
        Long finishAuditInstanceId = auditService.createAudit(createAuditRequestDTO);

        withdrawRecords.setAuditInstanceId(finishAuditInstanceId);
        iWithdrawRecordsService.updateWithdrawRecords(withdrawRecords);

        // 预扣除积分余额
        doctorService.addPoints(doctor.getId(),creditWithdrawDto.getCreditAmount().negate());






        return AjaxResult.success("提现已申请，审核后打款");
    }

    @Login
    @ApiOperation("医生提交讲者资质认证")
    @PostMapping("/launchSpeakerAuth")
    public AjaxResult launchSpeakerAuth(@RequestBody DoctorDto doctorDto) {
        String userId = getUserId();
        Doctor doctor = doctorService.selectDoctorById(Long.parseLong(userId));
        CommonCompanyInfo companyIdAndCompanyUserId = getCompanyIdAndCompanyUserId(doctor.getDoctorId());
        doctorDto.setCompanyUserId(companyIdAndCompanyUserId.getCompanyUserId());
        if (appDoctorService.add(doctorDto,Long.parseLong(userId)) != 1){
            return AjaxResult.error("讲者认证失败");
        }
        return AjaxResult.success("认证信息已提交");
    }

    @Login
    @ApiOperation("获取医生绑定的银行卡信息")
    @PostMapping("/getDockerBankInfo")
    public AjaxResult getDockerBankInfo() {
        String userId = getUserId();
        // 查询积分余额
        Doctor doctor = doctorService.selectDoctorById(Long.parseLong(userId));
        BankInfoVo bankInfoVo = BeanCopyUtils.copy(doctor, BankInfoVo.class);
        return AjaxResult.success(bankInfoVo);
    }

    @Login
    @ApiOperation("医生绑定自己的银行卡信息")
    @PostMapping("/addDockerBankInfo")
    public AjaxResult addDockerBankInfo(@RequestBody DockerBankDto dockerBankDto) {
        if (!validateBasicFormat(dockerBankDto.getBankCardNo())){
            return AjaxResult.error("银行卡号不合规，请检查");
        }
        String userId = getUserId();
        Doctor doctor = doctorService.selectDoctorById(Long.parseLong(userId));
        if (doctor.getBankCardNo() != null && doctor.getBankCardNo().equals("")){
            throw new CustomException("已绑定银行卡，无需再次绑定");
        }
        // 未绑则绑定
        doctor.setBankBranch(dockerBankDto.getBankBranch());
        doctor.setBankCardNo(dockerBankDto.getBankCardNo());
        doctor.setBankName(dockerBankDto.getBankName());

        appDoctorService.addBankCard(doctor);


        return AjaxResult.success("银行卡绑定成功");
    }

    /**
     * 基础格式校验
     * - 不为空
     * - 长度为13-19位（国际标准）
     * - 只包含数字
     */
    public static boolean validateBasicFormat(String bankCardNo) {
        if (bankCardNo == null || bankCardNo.trim().isEmpty()) {
            return false;
        }

        // 去除空格和特殊字符
        String cleanCardNo = bankCardNo.replaceAll("\\s+|-", "");

        // 长度校验（一般银行卡13-19位）
        if (cleanCardNo.length() < 13 || cleanCardNo.length() > 19) {
            return false;
        }

        // 只包含数字校验
        return cleanCardNo.matches("\\d+");
    }


}
