package org.dromara.app.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.dromara.app.domain.vo.AppConfigVo;
import org.dromara.app.service.AppLoginService;
import org.dromara.app.service.IAppAuthStrategy;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginBody;
import org.dromara.common.core.domain.model.RegisterBody;
import org.dromara.common.core.domain.model.SocialLoginBody;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils;
import org.dromara.member.domain.bo.AppUserBo;
import org.dromara.member.service.AppSocialAuthService;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.service.SysClientService;
import org.dromara.system.service.SysConfigService;
import org.dromara.web.domain.vo.LoginVo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * App端认证控制器
 *
 * @author pengles
 */
@Slf4j
@SaIgnore
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/auth")
public class AppAuthController {

    private final SocialProperties socialProperties;
    private final AppLoginService appLoginService;
    private final org.dromara.app.service.AppLoginRiskService appLoginRiskService;
    private final SysClientService clientService;
    private final SysConfigService configService;
    private final AppSocialAuthService appSocialAuthService;

    /**
     * App端登录方法
     *
     * @param body 登录信息
     * @return 结果
     */
    // TODO: App端暂时不加密，先完善用户端的加密方法在加密
    // @ApiEncrypt
    @PostMapping("/login")
    public R<LoginVo> login(@RequestBody String body) {
        LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
        ValidatorUtils.validate(loginBody);
        // 授权类型和客户端id
        String clientId = loginBody.getClientId();
        String grantType = loginBody.getGrantType();
        SysClientVo client = clientService.queryByClientId(clientId);
        // 查询不到 client 或 client 内不包含 grantType
        if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
            log.info("客户端id: {} 认证类型：{} 异常!.", clientId, grantType);
            return R.fail(MessageUtils.message("auth.grant.type.error"));
        } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
        }
        // App端登录
        LoginVo loginVo = IAppAuthStrategy.authenticate(body, client, grantType);

        Long userId = LoginHelper.getUserId();
        // 记录登录信息
        appLoginRiskService.checkRisk(userId, LoginHelper.getLoginUser().getIpaddr());
        appLoginService.recordLoginInfo(userId, LoginHelper.getLoginUser().getIpaddr());

        return R.ok(loginVo);
    }

    /**
     * App端退出登录
     *
     * @return 结果
     */
    @PostMapping("/logout")
    public R<Void> logout() {
        appLoginService.logout();
        return R.ok("退出成功");
    }

    /**
     * App端用户注册
     *
     * @param user 用户信息
     * @return 结果
     */
    // TODO: App端暂时不加密，先完善用户端的加密方法在加密
    // @ApiEncrypt
    @RateLimiter(key = "register", time = 60, count = 1)
    @PostMapping("/register")
    public R<Void> register(@Validated @RequestBody RegisterBody user) {
        if (!configService.selectRegisterEnabled()) {
            return R.fail("当前系统没有开启注册功能！");
        }
        AppUserBo bo = new AppUserBo();
        bo.setUserName(user.getUsername());
        bo.setPassword(user.getPassword());
        bo.setNickName(user.getUsername());

        // 校验是否已存在
        if (!appLoginService.checkUserNameUnique(bo.getUserName())) {
            return R.fail("注册失败，账号已存在");
        }

        // 执行注册
        return appLoginService.register(bo) ? R.ok() : R.fail("注册失败");
    }

    /**
     * 获取App端配置信息
     *
     * @return 配置信息
     */
    @GetMapping("/config")
    public R<AppConfigVo> getConfig() {
        AppConfigVo config = new AppConfigVo();
        config.setRegisterEnabled(configService.selectRegisterEnabled());
        // 可以添加更多App端特有的配置
        return R.ok(config);
    }

    /**
     * 获取登录二维码与票据
     */
    @GetMapping("/qrcode")
    public R<java.util.Map<String,String>> getQrCode() {
        String ticket = cn.hutool.core.util.IdUtil.fastSimpleUUID();
        RedisUtils.setCacheObject("APP_LOGIN_QR:" + ticket, "PENDING", java.time.Duration.ofMinutes(5));
        String url = "/wx/mp/qrcode/url?ticket=" + ticket;
        java.util.Map<String,String> res = new java.util.HashMap<>();
        res.put("ticket", ticket);
        res.put("url", url);
        return R.ok(res);
    }

    /**
     * 轮询二维码登录状态
     */
    @GetMapping("/qrcode/login")
    public R<String> pollQrLogin(@RequestParam String ticket) {
        String key = "APP_LOGIN_QR:" + ticket;
        String status = RedisUtils.getCacheObject(key);
        if (status == null) {
            return R.ok("EXPIRED");
        }
        return R.ok(status);
    }

    /**
     * 绑定第三方账号
     *
     * @param body 第三方账号信息
     * @return 结果
     */
    // TODO: App端暂时不加密，先完善前段的加密方法在加密
    // @ApiEncrypt
    @PostMapping("/bind/social")
    public R<Void> bindSocial(@RequestBody String body) {
        SocialLoginBody socialLoginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
        ValidatorUtils.validate(socialLoginBody);

        Long userId = LoginHelper.getUserId();
        if (userId == null) {
            return R.fail("请先登录");
        }

        try {
            // 获取第三方登录信息
            AuthResponse<AuthUser> response = SocialUtils.loginAuth(
                    socialLoginBody.getSource(), socialLoginBody.getSocialCode(),
                    socialLoginBody.getSocialState(), socialProperties);
            AuthUser authUserData = response.getData();
            // 判断授权响应是否成功
            if (!response.ok()) {
                return R.fail(response.getMsg());
            }
            // 执行绑定逻辑
            appSocialAuthService.socialRegister(authUserData);
            log.info("用户 {} 绑定第三方账号成功，平台: {}", userId, socialLoginBody.getSource());
            return R.ok("绑定成功");
        } catch (Exception e) {
            log.error("绑定第三方账号失败", e);
            return R.fail("绑定失败: " + e.getMessage());
        }
    }

    /**
     * 解绑第三方账号
     *
     * @param platform 第三方平台
     * @return 结果
     */
    @PostMapping("/unbind/social")
    public R<Void> unbindSocial(@RequestParam String platform) {
        Long userId = LoginHelper.getUserId();
        if (userId == null) {
            return R.fail("请先登录");
        }

        if (StringUtils.isBlank(platform)) {
            return R.fail("平台参数不能为空");
        }

        try {
            // 查找并删除绑定关系
            boolean success = appSocialAuthService.deleteByUserIdAndPlatform(userId, platform);
            if (success) {
                log.info("用户 {} 解绑第三方账号成功，平台: {}", userId, platform);
                return R.ok("解绑成功");
            } else {
                log.warn("用户 {} 尝试解绑不存在的第三方账号，平台: {}", userId, platform);
                return R.fail("未找到绑定关系");
            }
        } catch (Exception e) {
            log.error("用户 {} 解绑第三方账号失败，平台: {}", userId, platform, e);
            return R.fail("解绑失败: " + e.getMessage());
        }
    }

    /**
     * 获取App端第三方登录跳转URL
     *
     * @param source 登录来源
     * @param domain 域名
     * @return 结果
     */
    @GetMapping("/binding/{source}")
    public R<String> authBinding(@PathVariable("source") String source, @RequestParam String domain) {
        SocialLoginConfigProperties obj = socialProperties.getType().get(source);
        if (ObjectUtil.isNull(obj)) {
            return R.fail(source + "平台账号暂不支持");
        }
        AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
        Map<String, String> map = new HashMap<>();
        map.put("domain", domain);
        map.put("state", AuthStateUtils.createState());
        String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
        return R.ok("操作成功", authorizeUrl);
    }

    /**
     * App端社交账号绑定回调(需要token)
     *
     * @param loginBody 请求体
     * @return 结果
     */
    @PostMapping("/social/binding")
    public R<Void> socialBinding(@RequestBody SocialLoginBody loginBody) {
        // 校验token
        Long userId = LoginHelper.getUserId();
        if (userId == null) {
            return R.fail("请先登录");
        }

        try {
            // 获取第三方登录信息
            AuthResponse<AuthUser> response = SocialUtils.loginAuth(
                    loginBody.getSource(), loginBody.getSocialCode(),
                    loginBody.getSocialState(), socialProperties);
            AuthUser authUserData = response.getData();
            // 判断授权响应是否成功
            if (!response.ok()) {
                return R.fail(response.getMsg());
            }
            appSocialAuthService.socialRegister(authUserData);
            return R.ok("绑定成功");
        } catch (Exception e) {
            log.error("社交账号绑定失败", e);
            return R.fail("绑定失败: " + e.getMessage());
        }
    }

    /**
     * 获取用户绑定的第三方账号列表
     *
     * @return 绑定列表
     */
    @GetMapping("/social/list")
    public R<?> getSocialList() {
        Long userId = LoginHelper.getUserId();
        if (userId == null) {
            return R.fail("请先登录");
        }

        try {
            var socialList = appSocialAuthService.queryByUserId(userId);
            return R.ok(socialList);
        } catch (Exception e) {
            log.error("获取第三方账号绑定列表失败", e);
            return R.fail("获取失败: " + e.getMessage());
        }
    }


}
