package cn.novalon.gym.manage.member.handler;

import cn.novalon.gym.manage.common.exception.NotFoundException;
import cn.novalon.gym.manage.member.config.WechatProperties;
import cn.novalon.gym.manage.member.dto.AdminUpdatePhoneDto;
import cn.novalon.gym.manage.member.dto.SearchMemberDto;
import cn.novalon.gym.manage.member.dto.UpdateMemberInfoDto;
import cn.novalon.gym.manage.member.service.MemberService;
import cn.novalon.gym.manage.member.service.WechatAuthService;
import cn.novalon.gym.manage.member.service.WechatOfficialService;
import cn.novalon.gym.manage.member.util.AesUtil;
import cn.novalon.gym.manage.member.util.WechatPhoneUtil;
import cn.novalon.gym.manage.sys.util.AuthUtil;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

/**
 * 会员信息处理器
 *
 * @author 付嘉
 * @date 2026-05-01
 */

@Slf4j
@Component
@RequiredArgsConstructor
@Tag(name = "会员管理", description = "会员信息管理、微信绑定、服务号关注等")
public class MemberHandler {

    private final MemberService memberService;
    private final WechatAuthService wechatAuthService;
    private final WechatOfficialService wechatOfficialService;
    private final AuthUtil authUtil;

    @Operation(summary = "获取会员信息", description = "根据当前登录用户获取会员基本信息")
    public Mono<ServerResponse> getMemberInfo(ServerRequest request) {

        Long memberId = authUtil.getMemberIdOrThrow(request);

        log.info("获取会员信息, memberId: {}", memberId);

        return memberService.getMemberInfo(memberId)
                .flatMap(info -> ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_JSON)
                            .bodyValue(info))
                .onErrorResume(NotFoundException.class, e -> 
                    ServerResponse.status(HttpStatus.NOT_FOUND)
                            .contentType(MediaType.APPLICATION_JSON)
                            .bodyValue(java.util.Map.of("code", 404, "message", e.getMessage())));
    }

    @Operation(summary = "更新会员信息", description = "更新会员昵称、性别、生日、头像、地址等信息")
    public Mono<ServerResponse> updateMemberInfo(ServerRequest request) {

        Long memberId = authUtil.getMemberIdOrThrow(request);

        log.info("更新会员信息, memberId: {}", memberId);

        return request.bodyToMono(UpdateMemberInfoDto.class)
                .flatMap(updateDto -> memberService.updateMemberInfo(memberId, updateDto))
                .flatMap(info -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(info));
    }

    @Operation(summary = "绑定手机号", description = "通过微信小程序手机号code绑定会员手机号")
    public Mono<ServerResponse> bindPhone(ServerRequest request) {

        Long memberId = authUtil.getMemberIdOrThrow(request);

        String phoneCode = request.queryParam("phoneCode").orElse("");

        if (phoneCode.trim().isEmpty()) throw new IllegalArgumentException("手机号code不能为空");

        log.info("收到绑定手机号请求, memberId: {}, phoneCode: {}", memberId, phoneCode);

        return wechatAuthService.bindPhone(memberId, phoneCode)
                .flatMap(success -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(success));
    }

    @Operation(summary = "查询服务号关注状态", description = "查询会员是否关注微信服务号")
    public Mono<ServerResponse> checkSubscribeStatus(ServerRequest request) {

        Long memberId = authUtil.getMemberIdOrThrow(request);

        log.info("查询服务号关注状态, memberId: {}", memberId);

        return wechatOfficialService.checkSubscribeStatus(memberId)
                .flatMap(subscribed -> {
                    return ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_JSON)
                            .bodyValue(subscribed);
                });
    }

    @Operation(summary = "管理员更新手机号", description = "后台管理员为会员更新手机号")
    public Mono<ServerResponse> adminUpdatePhone(ServerRequest request) {

        Long adminId = authUtil.getMemberIdOrThrow(request);

        String memberIdStr = request.pathVariable("id");
        long memberId = NumberUtils.toLong(memberIdStr, 0L);

        if (memberId <= 0) throw new IllegalArgumentException("更新手机号失败: memberId 无效");

        log.info("收到更新手机号请求, memberId: {}", memberId);

        return request.bodyToMono(AdminUpdatePhoneDto.class)
                .flatMap(body -> {
                    String phone = body.getPhone();

                    if (phone == null || phone.isEmpty()) return Mono.error(new IllegalArgumentException("手机号不能为空"));

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

                    log.info("开始更新手机号, memberId: {}, phone: {}", memberId, phone);

                    return memberService.adminUpdatePhone(memberId, phone);
                })
                .flatMap(success -> {
                    log.info("手机号更新成功, memberId: {}", memberId);
                    return ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_JSON)
                            .bodyValue(success);
                });
    }

    @Operation(summary = "管理员查看会员详情", description = "后台管理员查看指定会员的详细信息")
    public Mono<ServerResponse> adminGetMemberInfo(ServerRequest request) {

        Long adminId = authUtil.getMemberIdOrThrow(request);

        String memberIdStr = request.pathVariable("id");
        long memberId = NumberUtils.toLong(memberIdStr, 0L);
        if(memberId <= 0) throw new IllegalArgumentException("会员ID格式错误");

        log.info("前台查看会员信息, adminId: {}, memberId: {}", adminId, memberId);

        return memberService.getMemberDetail(memberId)
                .flatMap(detail -> {
                    if (detail.getPhone() != null && !detail.getPhone().isEmpty()) {
                        try {
                            String decryptedPhone = AesUtil.decrypt(detail.getPhone());
                            detail.setPhone(WechatPhoneUtil.maskPhone(decryptedPhone));
                        } catch (Exception e) {
                            log.error("手机号解密失败, memberId: {}", detail.getId(), e);
                            detail.setPhone(null);
                        }
                    }
                    return ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(detail);
                });
    }

    @Operation(summary = "管理员编辑会员信息", description = "后台管理员编辑会员信息")
    public Mono<ServerResponse> adminUpdateMemberInfo(ServerRequest request) {

        Long adminId = authUtil.getMemberIdOrThrow(request);

        String memberIdStr = request.pathVariable("id");
        long memberId = NumberUtils.toLong(memberIdStr, 0L);
        if(memberId <= 0L) throw new IllegalArgumentException("会员ID格式错误");

        // TODO: 补充签到记录
        log.info("前台编辑会员信息, adminId: {}, memberId: {}", adminId, memberId);

        return request.bodyToMono(UpdateMemberInfoDto.class)
                .flatMap(updateDto -> memberService.adminUpdateMemberInfo(memberId, updateDto))
                .flatMap(detail -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(detail));
    }

    @Operation(summary = "搜索会员列表", description = "后台管理员按关键词搜索会员，支持性别筛选和分页")
    public Mono<ServerResponse> searchMembers(ServerRequest request) {

        Long adminId = authUtil.getMemberIdOrThrow(request);

        String keyword = request.queryParam("searchValue").orElse(null);
        Integer pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
        Integer pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);

        log.info("前台搜索会员列表, adminId: {}, keyword: {},pageNum: {}, pageSize: {}",
                adminId, keyword, pageNum, pageSize);

        return memberService.searchMember(new SearchMemberDto(keyword, pageNum, pageSize))
                .map(member -> {
                    // 解密手机号
                    if (member.getPhone() != null && !member.getPhone().isEmpty()) {
                        try {
                            String decryptedPhone = AesUtil.decrypt(member.getPhone());
                            member.setPhone(WechatPhoneUtil.maskPhone(decryptedPhone));
                        } catch (Exception e) {
                            log.error("手机号解密失败, memberId: {}", member.getId(), e);
                            member.setPhone(null);
                        }
                    }
                    return member;
                })
                .collectList()
                .flatMap(list -> ServerResponse.ok().bodyValue(list));
    }


    @Operation(summary = "查看会员列表", description = "后台管理员分页查看所有会员列表")
    public Mono<ServerResponse> getAllMembers(ServerRequest request) {

        Long adminId = authUtil.getMemberIdOrThrow(request);

        int pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
        int pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);

        log.info("前台查看会员列表, adminId: {}, pageNum: {}, pageSize: {}", adminId, pageNum, pageSize);
        // TODO: 补充签到记录

        return memberService.findAll(pageNum, pageSize)
                .map(member -> {
                    // 解密手机号
                    if (member.getPhone() != null && !member.getPhone().isEmpty()) {
                        try {
                            String decryptedPhone = AesUtil.decrypt(member.getPhone());
                            member.setPhone(WechatPhoneUtil.maskPhone(decryptedPhone));
                        } catch (Exception e) {
                            log.error("手机号解密失败, memberId: {}", member.getId(), e);
                            member.setPhone(null);
                        }
                    }
                    return member;
                })
                .collectList()
                .flatMap(list -> ServerResponse.ok().bodyValue(list));
    }

}
