// https://iwiki.woa.com/pages/viewpage.action?pageId=4007728669
// node version >= v12.22
// npm i jose@4.14.4
import { UserType } from '@gadmin2/nest-common';
import {
  CanActivate,
  ExecutionContext,
  Injectable,
  SetMetadata,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ApiSecurity } from '@nestjs/swagger';
import { DECORATORS_PREFIX } from '@nestjs/swagger/dist/constants';
import { getIdentity as getWoaIdentity } from './woaAuth';
import YuFuId, { GAdminTokenName } from './yufuid';

const API_SECURITY = `${DECORATORS_PREFIX}/apiSecurity`;
export function ApiKeyAuth(name = 'X-API-KEY') {
  return ApiSecurity(name);
}

export const AllowUnauthorizedRequest = () =>
  SetMetadata('allowUnauthorizedRequest', true);

export const AllowApiKeyRequest = () => SetMetadata('allowApiKeyRequest', true);

async function getIdentity(headers, cookies, key): Promise<UserType> {
  // 先验证woa登录态
  try {
    const { staffid, staffname } = await getWoaIdentity(headers, key);
    if (staffid) {
      return { userid: staffid, username: staffname };
    }
  } catch (e) {
    console.warn('woa auth failed, try yufu auth...');
  }
  // 再验证玉符登录态
  const token = headers[GAdminTokenName] || cookies[GAdminTokenName];
  const userInfo = await YuFuId.verify(token);
  console.log('yufu auth success', userInfo);
  return { userid: userInfo.sub, username: userInfo.sub };
}

// apikey genertor https://codepen.io/corenominal/pen/rxOmMJ
const API_KEYS = [
  {
    userid: 'steam_session',
    username: 'steam_session',
    apiKey: '9c5fc474-9e95-480b-b380-5e15f77b5f9f',
  },
];
async function getApikeyIdentity(req): Promise<UserType> {
  const apiKey = req.headers['x-api-key'] ?? req.query.api_key;
  if (apiKey) {
    for (const item of API_KEYS) {
      if (item.apiKey === apiKey) {
        console.log('apikey auth success:', item.userid);
        return { userid: item.userid, username: item.username };
      }
    }
  }
  throw new UnauthorizedException(`error apikey: ${apiKey}`);
}

@Injectable()
export default class AuthGuard implements CanActivate {
  constructor(private readonly token: string, private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean | Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const allowUnauthorizedRequest = this.reflector.get<boolean>(
      'allowUnauthorizedRequest',
      context.getHandler(),
    );
    if (allowUnauthorizedRequest) {
      return true;
    }

    const allowApiKeyRequest = this.reflector.get<boolean>(
      API_SECURITY,
      context.getHandler(),
    );

    return (
      allowApiKeyRequest
        ? getApikeyIdentity(request)
        : Promise.reject("don't allow api key request.")
    )
      .catch((error) => {
        console.warn(error, 'try woa auth...');

        return getIdentity(
          request.headers,
          request.cookies,
          Buffer.from(this.token),
        );
      })
      .then((userInfo) => {
        request.user = userInfo;
        return true;
      })
      .catch((error) => {
        console.error(error);
        throw new UnauthorizedException(error);
      });
  }
}
