package com.xunhong.erp.turbo.auth.controller;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.config.SaCookieConfig;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaCookie;
import cn.dev33.satoken.sso.template.SaSsoUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.xunhong.erp.turbo.api.facade.api.WxMaServiceI;
import com.xunhong.erp.turbo.api.facade.dto.qry.WxMaOpenidQry;
import com.xunhong.erp.turbo.api.facade.dto.vo.WxMaOpenidVO;
import com.xunhong.erp.turbo.api.infra.api.PlatformServiceI;
import com.xunhong.erp.turbo.api.infra.dto.qry.PlatformListQry;
import com.xunhong.erp.turbo.api.infra.dto.vo.PlatformVO;
import com.xunhong.erp.turbo.api.rbac.api.UserPermissionServiceI;
import com.xunhong.erp.turbo.api.rbac.api.UserRoleServiceI;
import com.xunhong.erp.turbo.api.rbac.dto.qry.UserPermissionQry;
import com.xunhong.erp.turbo.api.rbac.dto.qry.UserRoleListQry;
import com.xunhong.erp.turbo.api.rbac.dto.vo.PermissionVO;
import com.xunhong.erp.turbo.api.rbac.dto.vo.UserRoleVO;
import com.xunhong.erp.turbo.api.user.api.AdminServiceI;
import com.xunhong.erp.turbo.api.user.api.UserAuthServiceI;
import com.xunhong.erp.turbo.api.user.dto.cmd.OpenSafeCmd;
import com.xunhong.erp.turbo.api.user.dto.cmd.UpdatePasswordCmd;
import com.xunhong.erp.turbo.api.user.dto.qry.AdminShowQry;
import com.xunhong.erp.turbo.api.user.dto.qry.PasswordLoginQry;
import com.xunhong.erp.turbo.api.user.dto.vo.AdminVO;
import com.xunhong.erp.turbo.api.user.dto.vo.UserAuthVO;
import com.xunhong.erp.turbo.auth.exception.UserAuthErrorCode;
import com.xunhong.erp.turbo.base.exception.BizException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author shenyifei
 */
@Tag(name = "UserAuth", description = "认证管理")
@RestController("authUserAuthController")
@RequestMapping("/auth")
@RequiredArgsConstructor
public class UserAuthController {

	@DubboReference(version = "1.0.0")
	private final UserAuthServiceI userAuthService;

	@DubboReference(version = "1.0.0")
	private final PlatformServiceI platformService;

	@DubboReference(version = "1.0.0")
	private final UserPermissionServiceI userPermissionService;

	@DubboReference(version = "1.0.0")
	private final UserRoleServiceI userRoleService;

	@DubboReference(version = "1.0.0")
	private final WxMaServiceI wxMaService;

	private final CaptchaService captchaService;

	@DubboReference(version = "1.0.0")
	private final AdminServiceI adminService;

	private static PlatformVO getPlatformVO(String redirect, List<PlatformVO> platformVOList) {
		if (StrUtil.isBlankIfStr(redirect)) {
			return platformVOList.getFirst();
		}

		// 已登录情况下，构建 redirectUrl
		redirect = SaFoxUtil.decoderUrl(redirect);

		PlatformVO platformVO;
		// 编译正则表达式以匹配URL中的域名部分
		Pattern pattern = Pattern.compile("(https?://[^/]+)");
		Matcher matcher = pattern.matcher(redirect);
		// 如果找到了匹配的域名部分
		if (matcher.find()) {
			String domain = matcher.group(1);
			platformVO = platformVOList.stream()
				.filter(merchantAppVO1 -> merchantAppVO1.getHomePage().contains(domain))
				.findFirst()
				.orElse(platformVOList.getFirst());

		} else {
			platformVO = platformVOList.getFirst();
		}
		return platformVO;
	}

	private static void addCookie(String name, String value, int timeout) {
		// 获取cookie配置信息
		SaCookieConfig cfg = SaManager.getConfig().getCookie();

		// 创建SaCookie对象并设置相关属性
		SaCookie cookie = new SaCookie()
			.setName(name) // 设置cookie名称为"merchantId"
			.setValue(value) // 将商户ID转换为字符串并设置为cookie的值
			.setMaxAge(timeout) // 设置cookie的存活时间，单位为秒
			.setDomain(cfg.getDomain()) // 设置cookie的域名
			.setPath(cfg.getPath()) // 设置cookie的路径
			.setSecure(cfg.getSecure()) // 设置是否仅通过HTTPS传输cookie
			.setHttpOnly(cfg.getHttpOnly()) // 设置是否禁止JavaScript访问cookie
			.setSameSite(cfg.getSameSite()); // 设置SameSite属性，控制cookie的发送范围

		// 将创建的cookie添加到HTTP响应中
		SaHolder.getResponse().addCookie(cookie);
	}

	/**
	 * 密码登录
	 *
	 * @param passwordLoginQry 用户密码登录请求
	 * @return 返回
	 */
	@PostMapping("/passwordLogin")
	@Operation(summary = "账号密码登录接口", method = "POST")
	public SingleResponse<UserAuthVO> passwordLogin(@RequestBody @Validated PasswordLoginQry passwordLoginQry) {
		checkCaptchaVerification(passwordLoginQry.getToken());

		UserAuthVO userAuthVO = userAuthService.passwordLogin(passwordLoginQry);

		extracted(userAuthVO);
		return SingleResponse.of(userAuthVO);
	}

	/**
	 * 修改密码
	 */
	@SaCheckLogin
	@PostMapping("/updatePassword")
	@Operation(summary = "修改密码", method = "POST")
	public Response updatePassword(@RequestBody @Validated UpdatePasswordCmd updatePasswordCmd) {
		checkCaptchaVerification(updatePasswordCmd.getToken());

		userAuthService.updatePassword(updatePasswordCmd);

		return Response.buildSuccess();
	}

	/**
	 * 微信登录
	 *
	 * @return 返回
	 */
	@PostMapping("/wxMaCodeLogin")
	@Operation(summary = "微信登录", method = "POST")
	public SingleResponse<UserAuthVO> wxMaCodeLogin(@RequestBody @Validated WxMaOpenidQry wxMaOpenidQry) {
		WxMaOpenidVO wxMaOpenidVO = wxMaService.getOpenId(wxMaOpenidQry);
		wxMaOpenidQry.setMaOpenid(wxMaOpenidVO.getOpenid());
		wxMaOpenidQry.setUnionid(wxMaOpenidVO.getUnionid());
		UserAuthVO userAuthVO = userAuthService.wxMaCodeLogin(wxMaOpenidQry);
		extracted(userAuthVO);

		return SingleResponse.of(userAuthVO);
	}

	/**
	 * 当前是否登录
	 */
	@PostMapping("/sso/isLogin")
	@Operation(summary = "当前是否登录", method = "POST")
	public Object isLogin() {
		return SaResult.data(StpUtil.isLogin());
	}

	/**
	 * 登出
	 *
	 * @return 返回
	 */
	@SaCheckLogin
	@PostMapping("/sso/logout")
	@Operation(summary = "登出", method = "POST")
	public Response logout() {
		StpUtil.logout();
//		SaSsoServerProcessor.instance.ssoSignout();
		addCookie("satoken", "", 0);
		return Response.buildSuccess();
	}

	/**
	 * 返回SSO认证中心登录地址
	 */
	@PostMapping("/sso/getSsoAuthUrl")
	@Operation(summary = "返回SSO认证中心登录地址", method = "POST")
	public SingleResponse<String> getSsoAuthUrl(String clientLoginUrl) {
		String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, "");
		return SingleResponse.of(serverAuthUrl);
	}

	/**
	 * 获取 redirectUrl
	 */
	@PostMapping("/sso/getRedirectUrl")
	@Operation(summary = "获取 redirectUrl", method = "POST")
	public SingleResponse<String> getRedirectUrl(String redirect) {
		// 未登录情况下，返回 code=401
		if (!StpUtil.isLogin()) {
			throw new BizException(UserAuthErrorCode.USER_AUTH_NOT_LOGIN);
		}

		List<PlatformVO> platformVOList = platformService.list(new PlatformListQry());

		PlatformVO platformVO = getPlatformVO(redirect, platformVOList);

		UserAuthVO userAuthVO = (UserAuthVO) StpUtil.getSession().get("userAuth");
		List<UserRoleVO> userRoleVOList = userAuthVO.getUserRoleVOList();

		// 查询用户关联的权限
		Long platformId = platformVO.getPlatformId();
		UserPermissionQry userPermissionQry = new UserPermissionQry();
		userPermissionQry.setPlatformId(platformId);
		userPermissionQry.setRoleIdList(userRoleVOList.stream().map(UserRoleVO::getRoleId).toList());
		List<PermissionVO> permissionVOList = userPermissionService.list(userPermissionQry);

		StpUtil.getSession().set("platformId:" + platformId, permissionVOList);

		return SingleResponse.of(platformVO.getHomePage());
	}

	/**
	 * 登录后处理
	 *
	 * @param userAuthVO 用户信息
	 */
	private void extracted(UserAuthVO userAuthVO) {
		// 查询用户关联的角色
		UserRoleListQry userRoleListQry = new UserRoleListQry();
		userRoleListQry.setUserId(userAuthVO.getUserId());
		List<UserRoleVO> userRoleVOList = userRoleService.list(userRoleListQry);
		userAuthVO.setUserRoleVOList(userRoleVOList);

		// 登录, 同端互斥
		StpUtil.login(userAuthVO.getUserId());
		StpUtil.getSession().set("userAuth", userAuthVO);

		AdminShowQry adminShowQry = new AdminShowQry();
		adminShowQry.setUserId(userAuthVO.getUserId());
		AdminVO adminVO = adminService.show(adminShowQry);
		StpUtil.getSession().set("createdBy", adminVO.getAdminId());
		StpUtil.getSession().set("createdByName", adminVO.getName());

		SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
		UserAuthVO.Meta meta = new UserAuthVO.Meta();
		meta.setAccessToken(tokenInfo.getTokenValue());
		meta.setExpiresIn(tokenInfo.getTokenTimeout());
		meta.setTokenType("Bearer");

		userAuthVO.setMeta(meta);
	}

	/**
	 * 检查验证码验证
	 * 本方法用于验证用户提交的验证码是否有效通过验证码服务的验证
	 *
	 * @param token 验证码令牌，用于验证用户提交的验证码
	 * @throws BizException 如果验证码验证失败，抛出业务异常
	 */
	private void checkCaptchaVerification(String token) {
		// 初始化验证码校验对象
		CaptchaVO captchaVO = new CaptchaVO();
		// 设置验证码验证令牌
		captchaVO.setCaptchaVerification(token);
		// 调用验证码服务进行验证
		ResponseModel verification = captchaService.verification(captchaVO);
		// 如果验证失败，抛出业务异常
		if (!verification.isSuccess()) {
			throw new BizException(verification.getRepMsg(), UserAuthErrorCode.CAPTCHA_ERROR);
		}
	}

	@PostMapping("/openSafe")
	@Operation(summary = "二次认证", method = "POST")
	public Response openSafe(@RequestBody @Validated OpenSafeCmd openSafeCmd) {
		openSafeCmd.setUserId(StpUtil.getLoginIdAsLong());
		userAuthService.openSafe(openSafeCmd);

		StpUtil.openSafe(600);
		return Response.buildSuccess();
	}
}
