import { getCurrentInstance, ref } from "vue";
import { apiService } from "./api.service";
import oauthApi from "../constants/api/oauth.api";
import { RestfulResponse } from "../model/domain/RestfulResponse";
import { OauthConfig } from "../model/domain/OauthConfig";
import { tokenStore } from "../stores/token.store";
import { Token } from "../model/domain/Token";
import { Code } from "../constants/enum/code.enum";
import { RestApi } from "../model/domain/RestApi";
import { SyncCacheRequest } from "../model/domain/SyncCacheRequest";
import { IdentityType } from "../constants/enum/identity-type.enum";
import { routeTopologyStore } from "../stores/route-topology.store";
import { userStore } from "../stores/user.store";
import sysUserInfoApi from "../constants/api/sys-user-info.api";
import { CasConfig } from "../model/domain/CasConfig";
import { CoreConfig } from "../model/domain/CoreConfig";
import { SysUserInfo } from "../model/entity/SysUserInfo";
import { urlUtilService } from "../services/url-util.service";
import { redirectUriStore } from "../stores/redirect-uri.store";
import { StatusCode } from "../constants/enum/status-code.enum";
import { LoginRequest } from "../model/domain/LoginRequest";
import { LoginResponse } from "../model/domain/LoginResponse";
import { coreEventService } from "../services/core-event.service";
import { AuthEventType } from "../constants/enum/auth-event-type.enum";
import { AuthType } from "../constants/enum/auth-type.enum";
import { WxQyOauth } from "../model/domain/WxQyOauth";
import { AppContext } from "@vue/runtime-core";

/**
 * @description: 授权服务
 * @author ChenRui
 * @date 2020/8/27 10:48
 */
class OauthService {
  isLogin = ref(false); // 是否登录
  private appContext: AppContext | undefined;
  static readonly BASE_TOKEN_TYPE: string = "Basic ";
  static readonly AUTH_EVENT_NAME: string = "auth";

  /**
   * @description: 获取全局配置
   * @author ChenRui
   * @date 2022/5/25 17:55
   */
  get coreConfig(): Partial<CoreConfig> {
    if (!this.appContext) {
      this.appContext = getCurrentInstance()?.appContext;
    }
    if (this.appContext && this.appContext.config.globalProperties.$dc != null) {
      return this.appContext.config.globalProperties.$dc;
    }
    return {};
  }

  /**
   * @description: 全局配置-auth
   * @author ChenRui
   * @date 2021/8/6 15:59
   */
  get authConfig(): OauthConfig {
    if (this.coreConfig != null && this.coreConfig.oauth != null) {
      return this.coreConfig.oauth;
    }
    return new OauthConfig();
  }

  /**
   * @description: 全局配置-企业微信auth
   * @author ChenRui
   * @date 2021/8/6 15:59
   */
  get wxQyOauth(): WxQyOauth {
    if (this.coreConfig != null && this.coreConfig.wxQyOauth != null) {
      return this.coreConfig.wxQyOauth;
    }
    return new WxQyOauth();
  }

  /**
   * @description: 全局配置-cas
   * @author ChenRui
   * @date 2021/8/6 15:59
   */
  get casConfig(): CasConfig {
    if (this.coreConfig != null && this.coreConfig.cas && this.coreConfig.oauth != null) {
      return this.coreConfig.cas;
    }
    return new CasConfig();
  }

  /**
   * @description: 检查登录状态并进行登录
   * @author ChenRui
   * @date 2021/8/4 10:52
   */
  checkAndLogin(): Promise<RestfulResponse> {
    if (this.coreConfig.authPreferTo === AuthType.OAUTH.code) {
      if (this.authConfig.enableNoticeMode) {
        if (!this.isLoadedToken) {
          // 通知宿主进行登录
          setTimeout(() => {
            coreEventService.emit(OauthService.AUTH_EVENT_NAME, { type: AuthEventType.LOGIN.code });
          });
          return Promise.resolve(RestfulResponse.failure("正在前往登录"));
        } else if (!this.isTokenExpiration) {
          // 判断用户令牌是否有效
          // 若用户已经登录但令牌过期，此时需要使用refresh_token进行令牌刷新
          return this.refreshToken();
        }
      } else {
        if (!this.isLoadedToken) {
          // 判断用户是否登录
          const code = urlUtilService.getQueryValue("code");
          const state = urlUtilService.getQueryValue("state");
          if (code && state === this.authConfig.state) {
            // 在授权中心登录成功后重定向回用户页面，检查url是否包含授权码与自定义状态码,如果包含则使用授权码向授权服务器请求用户令牌
            return this.getTokenByAuthCode();
          } else if (this.wxQyOauth.enable && code && state === this.wxQyOauth.state) {
            // 由企业微信授权后重定向回用户界面，检查url是否包含授权码与自定义状态码,如果包含则使用授权码向后端指定接口兑换用户信息
            return this.getTokenByWxQyAuthCode();
          } else {
            // 本地尚未存储令牌，同时当前url也未包含授权码，则导航到授权中心进行登录
            return this.login();
          }
        } else if (!this.isTokenExpiration) {
          // 判断用户令牌是否有效
          // 若用户已经登录但令牌过期，此时需要使用refresh_token进行令牌刷新
          return this.refreshToken();
        }
      }
      return Promise.resolve(RestfulResponse.success(null, "用户已登录"));
    } else if (this.coreConfig.authPreferTo === AuthType.CAS.code) {
      // 判断用户是否登录
      if (!this.isLoadedToken) {
        const voucher = urlUtilService.getQueryValue(this.casConfig.voucherKey);
        if (voucher) {
          // 检查url是否包含三方cas系统认证后提供的voucher票据,如果包含则使用voucher票据想指定服务器请求用户信息
          return this.getTokenByAuthCasVoucher(voucher);
        } else {
          // 本地尚未存储令牌，同时当前url也未包含授权码，则导航到授权中心进行登录
          return this.caslogin();
        }
      } else if (!this.isTokenExpiration) {
        // 判断用户令牌是否有效
        // 若用户已经登录但令牌过期，此时需要使用refresh_token进行令牌刷新
        return this.refreshToken();
      }
      return Promise.resolve(RestfulResponse.success(null, "用户已登录"));
    } else {
      return Promise.resolve(RestfulResponse.failure("尚不支持当前的认证类型"));
    }
  }

  /**
   * @description: 通过授权码获取令牌
   * @author ChenRui
   * @date 2021/8/4 11:53
   */
  getTokenByAuthCode(): Promise<RestfulResponse> {
    const query: any = {
      grant_type: "authorization_code",
      code: urlUtilService.getQueryValue("code"),
      redirect_uri: this.genRedirectUri(),
    };

    const basicAuth: any = this.genBasicAuth(); // 基础认证消息头

    const restApi: RestApi = new RestApi(oauthApi.token);
    restApi.url = this.authConfig.authorizeUrl + oauthApi.token.url;
    return apiService.general(restApi, query, undefined, basicAuth).then((token: any) => {
      if (token && token.access_token && token.jti) {
        tokenStore.token = new Token(token); // 缓存令牌信息
        return Promise.resolve(this.syncUserCache(token));
      } else {
        tokenStore.token = undefined; // 清空缓存的令牌信息
        userStore.user = undefined; // 清空用户信息
        return RestfulResponse.failure(token && token.msg ? token.msg : "令牌加载失败");
      }
    });
  }

  /**
   * @description:
   * @author ChenRui
   * @date 2022/2/26 20:46
   */
  getTokenByAuthCasVoucher(voucher: string): Promise<RestfulResponse> {
    const param: any = {
      [this.casConfig.voucherKey]: voucher,
    };

    const basicAuth: any = this.genBasicAuth(); // 基础认证消息头

    const restApi: RestApi = new RestApi(oauthApi.token);
    restApi.url = this.coreConfig.baseApi + this.casConfig.innerLoginUri;
    return apiService.general(restApi, undefined, param, basicAuth).then((res: any) => {
      if (res.code === Code.SUCCESS.code) {
        tokenStore.token = new Token(res.data?.token); // 缓存令牌信息
        userStore.user = res.data?.sysUserInfo; // 缓存用户信息
        return res;
      } else {
        tokenStore.token = undefined; // 清空缓存的令牌信息
        userStore.user = undefined; // 清空用户信息
        return RestfulResponse.failure(res && res.msg ? res.msg : "CAS登录失败");
      }
    });
  }

  /**
   * @description: 通过企业微信获取授权码
   * @author ChenRui
   * @date 2022/2/26 20:46
   */
  getTokenByWxQyAuthCode(): Promise<RestfulResponse> {
    const query: any = {
      code: urlUtilService.getQueryValue("code"),
    };
    const restApi: RestApi = new RestApi(sysUserInfoApi.qyWechatOauthLogin);
    restApi.url = this.coreConfig.baseApi + sysUserInfoApi.qyWechatOauthLogin.url;
    return apiService.general(restApi, query).then((res: any) => {
      if (res.code === Code.SUCCESS.code) {
        tokenStore.token = new Token(res.data?.token); // 缓存令牌信息
        userStore.user = res.data?.sysUserInfo; // 缓存用户信息
        return res;
      } else {
        tokenStore.token = undefined; // 清空缓存的令牌信息
        userStore.user = undefined; // 清空用户信息
        return RestfulResponse.failure(res && res.msg ? res.msg : "企业微信登录失败");
      }
    });
  }

  /**
   * @description: 单点登录
   * @author ChenRui
   * @date 2021/8/4 10:49
   */
  login(): Promise<RestfulResponse> {
    const authorizeUrl = this.authConfig.authorizeUrl + oauthApi.authorize.url;
    const param = {
      response_type: "code",
      client_id: this.authConfig.clientId,
      scope: this.authConfig.scope,
      state: this.authConfig.state,
      redirect_uri: this.genRedirectUri(),
    };
    window.location.href = apiService.urlQueryConvert(authorizeUrl, param);
    return Promise.resolve(RestfulResponse.failure("用户未登录，正在前往登录入口"));
  }

  /**
   * @description: 用户登录-密码模式
   * @author ChenRui
   * @date 2022/1/25 17:24
   */
  simpleLogin(loginRequest: LoginRequest): Promise<any> {
    const restApi: RestApi = new RestApi(sysUserInfoApi.login);
    const url = this.authConfig.simpleLoginUri ? this.authConfig.simpleLoginUri : sysUserInfoApi.login.url;
    restApi.url = this.coreConfig.baseApi + url;

    return apiService.general(restApi, undefined, loginRequest).then((response: RestfulResponse<LoginResponse>) => {
      if (response.code === Code.SUCCESS.code) {
        userStore.user = response.data?.sysUserInfo; // 缓存用户信息
        tokenStore.token = new Token(response.data?.token); // 缓存令牌信息
      } else {
        userStore.user = undefined; // 缓存用户信息
        tokenStore.token = undefined; // 缓存令牌信息
      }
      return response;
    });
  }

  /**
   * @description: 扩展登录模式-cas登录
   * @author ChenRui
   * @date 2022/2/26 20:44
   */
  caslogin(): Promise<RestfulResponse> {
    const authorizeUrl = this.casConfig.authorizeUrl;
    const param = {
      [this.casConfig.redirectUriKey]: this.genRedirectUri(),
    };
    window.location.href = apiService.urlQueryConvert(authorizeUrl, param);
    return Promise.resolve(RestfulResponse.failure("用户未登录，正在前往登录入口"));
  }

  /**
   * @description: 刷新令牌
   * @author ChenRui
   * @date 2021/8/4 17:57
   */
  refreshToken(): Promise<RestfulResponse> {
    if (this.authConfig.enableNoticeMode) {
      const params = {
        userId: userStore.user?.userId,
        refreshToken: tokenStore.token?.refreshToken,
        userIdentity: tokenStore.token?.jti,
      };
      const restApi: RestApi = new RestApi(sysUserInfoApi.refreshToken);
      restApi.url = this.coreConfig.baseApi + sysUserInfoApi.refreshToken.url;
      return apiService.general(restApi, undefined, params).then((response: RestfulResponse) => {
        if (response && response.code === Code.SUCCESS.code) {
          tokenStore.token = new Token(response.data); // 缓存令牌信息
          return this.syncUserCache(response.data);
        } else {
          tokenStore.token = undefined; // 清空缓存的令牌信息
          userStore.user = undefined; // 清空用户信息
          return new RestfulResponse({
            code: Code.FAIL.code,
            msg: "令牌刷新失败",
            meta: {
              statusCode: StatusCode.REFRESH_TOKEN_FAILED.code,
            },
          });
        }
      });
    } else {
      const basicAuth: any = this.genBasicAuth(); // 基础认证消息头

      const restApi: RestApi = new RestApi(oauthApi.token);
      restApi.url = this.authConfig.authorizeUrl + oauthApi.token.url;
      const query: any = {
        grant_type: "refresh_token",
        refresh_token: tokenStore.token?.refreshToken,
      };
      return apiService.general(restApi, query, undefined, basicAuth).then((token: Token) => {
        console.log(token);
        if (token && token.access_token && token.jti) {
          tokenStore.token = new Token(token); // 缓存令牌信息
          return this.syncUserCache(token);
        } else {
          tokenStore.token = undefined; // 清空缓存的令牌信息
          userStore.user = undefined; // 清空用户信息
          return new RestfulResponse({
            code: Code.FAIL.code,
            msg: "令牌刷新失败",
            meta: {
              statusCode: StatusCode.REFRESH_TOKEN_FAILED.code,
            },
          });
        }
      });
    }
  }

  /**
   * @description: 登出
   * @author ChenRui
   * @date 2020/12/18 17:53
   */
  logOut(authorizeUrl?: string): Promise<RestfulResponse> {
    // 清空服务器端的用户数据缓存
    if (userStore.user && userStore.user.userId) {
      const query = {
        userId: userStore.user.userId,
        userIdentity: tokenStore.token?.jti,
      };
      const restApi: RestApi = new RestApi(sysUserInfoApi.logout);
      restApi.url = this.coreConfig.baseApi + sysUserInfoApi.logout.url;
      return apiService
        .general(restApi, query)
        .then((response: RestfulResponse) => {
          return this.cleanOauthUserInfo();
        })
        .catch(() => {
          return this.cleanOauthUserInfo();
        });
    } else {
      return this.cleanOauthUserInfo();
    }
  }

  /**
   * @description: 生成重定向地址
   * @author ChenRui
   * @date 2021/8/6 17:36
   */
  genRedirectUri(isUseCache = true): string {
    if (this.authConfig.redirectUri) {
      return this.authConfig.redirectUri;
    } else {
      if (isUseCache && !!redirectUriStore.redirectUri) {
        return redirectUriStore.redirectUri;
      } else {
        const code = urlUtilService.getQueryValue("code");
        const state = urlUtilService.getQueryValue("state");
        if (code && state === this.authConfig.state) {
          const url = urlUtilService.deleteParameter(window.location.href, "code", "state");
          redirectUriStore.redirectUri = url;
        } else {
          redirectUriStore.redirectUri = window.location.href;
        }
      }
      return redirectUriStore.redirectUri;
    }
  }

  /**
   * @description: 清空授权中心缓存数据
   * @author ChenRui
   * @date 2021/8/6 18:20
   */
  private cleanOauthUserInfo(): Promise<RestfulResponse> {
    routeTopologyStore.inited = false; // 清空路由拓扑
    userStore.user = undefined;
    tokenStore.token = undefined;
    if (this.authConfig.enableNoticeMode) {
      return Promise.resolve(RestfulResponse.success(null, "登出成功"));
    } else {
      const restApi: RestApi = new RestApi(oauthApi.ssoLogout);
      restApi.url = this.authConfig.authorizeUrl + oauthApi.ssoLogout.url;
      setTimeout(() => {
        window.location.href = restApi.url + "?redirect_uri=" + this.genRedirectUri(false);
        redirectUriStore.redirectUri = undefined;
      }, 100);
      return Promise.resolve(RestfulResponse.success(null, "登出成功"));
    }
  }

  /**
   * @description: 同步用户信息
   * @author ChenRui
   * @date 2021/8/4 21:23
   */
  private syncUserCache(token: Token): Promise<RestfulResponse> {
    const params: SyncCacheRequest = {
      userName: token.userName,
      userIdentity: token.jti,
      identityType: this.authConfig.identityType || IdentityType.password.code,
    };
    const restApi: RestApi = new RestApi(sysUserInfoApi.syncUserCache);
    restApi.url = this.coreConfig.baseApi + sysUserInfoApi.syncUserCache.url;

    return apiService.general(restApi, undefined, params).then((response: RestfulResponse) => {
      if (response.code === Code.SUCCESS.code) {
        userStore.user = response.data; // 缓存用户信息
      }
      return response;
    });
  }

  /**
   * @description: 创建基础认证消息头
   * @author ChenRui
   * @date 2021/8/4 21:18
   */
  private genBasicAuth(): any {
    const clientCredentials: string = this.authConfig.clientId + ":" + this.authConfig.clientSecret;
    const authorization = OauthService.BASE_TOKEN_TYPE + window.btoa(clientCredentials);
    const basicAuth: any = {
      headers: {
        Authorization: authorization,
      },
    };
    return basicAuth;
  }

  /**
   * @description: 检查是否获加载完令牌
   * @author ChenRui
   * @date 2020/8/27 11:08
   */
  get isLoadedToken(): boolean {
    const token: Token | undefined = tokenStore.token;
    return token != null && !!token.accessToken;
  }

  /**
   * @description: 检查是否获加载完令牌
   * @author ChenRui
   * @date 2021/8/6 23:36
   */
  get isLoadedUserInfo(): boolean {
    const sysUserInfo: SysUserInfo | undefined = userStore.user;
    return sysUserInfo != null && !!sysUserInfo.userId;
  }

  /**
   * @description: 检查令牌是否有效
   * @author ChenRui
   * @date 2021/8/4 11:13
   */
  get isTokenExpiration(): boolean {
    const token: Token | undefined = tokenStore.token;
    if (token != null && token.expiresIn) {
      const currentTimeStamp = new Date().getTime();
      const expiratTimeStamp = Number(token.time) + Number((token.expiresIn - 300) * 1000);
      return !(currentTimeStamp > expiratTimeStamp);
    }
    return false;
  }
}
const oauthService = new OauthService();
export { OauthService, oauthService };
