import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import type { FastifyRequest } from 'fastify';
import { RedisDatabase, RedisService } from '../../plugins/redis/redis.service';
import { LoggerService } from '../../plugins/logger/logger.service';

@Injectable()
export class AuthGuard implements CanActivate {
  public constructor(
    private readonly redisService: RedisService,
    private readonly logger: LoggerService
  ) {}

  public async canActivate(context: ExecutionContext): Promise<boolean> {
    const http = context.switchToHttp();
    const req = http.getRequest<FastifyRequest>();

    const url = String((req as any).url || '');
    if (url.startsWith('/api-docs')) {
      return true;
    }

    const headers = (req.headers || {}) as Record<string, any>;
    // ✅ 兼容大小写
    const authorization = String(headers.authorization || headers.Authorization || '');
    const customMac = String(headers['custom-mac'] || headers['Custom-Mac'] || '');
    const customPlatform = String(headers['custom-platform'] || headers['Custom-Platform'] || '');
    const customTimestamp = String(headers['custom-timestamp'] || headers['Custom-Timestamp'] || '');

    if (!authorization || !authorization.toLowerCase().startsWith('bearer ')) {
      throw new UnauthorizedException('缺少或无效的Authorization');
    }
    if (!customMac) {
      throw new UnauthorizedException('缺少Custom-Mac');
    }
    if (!customPlatform) {
      throw new UnauthorizedException('缺少Custom-Platform');
    }
    if (!customTimestamp) {
      throw new UnauthorizedException('缺少Custom-Timestamp');
    }

    const token = authorization.slice(7).trim();
    const parts = token.split('.');

    if (parts.length < 2) {
      throw new UnauthorizedException('无效的Token格式');
    }

    const payload: any = (() => {
      try {
        const json = Buffer.from(parts[1], 'base64url').toString('utf8');
        return JSON.parse(json);
      } catch {
        throw new UnauthorizedException('无法解析Token');
      }
    })();
    const NAME_ID_CLAIM = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier';
    const ROLE_CLAIM = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role';
    const userId = String(payload?.[NAME_ID_CLAIM] || '');
    const role = Number(payload?.[ROLE_CLAIM] || '-1');
    if (!userId || isNaN(role) || role < 0) {
      throw new UnauthorizedException('Token非法');
    }

    const nowSec = Math.floor(Date.now() / 1000);
    const exp = Number(payload?.exp || 0);
    if (!Number.isFinite(exp) || nowSec >= exp) {
      throw new UnauthorizedException('Token已过期');
    }

    const platform = customPlatform.toUpperCase();

    const redisKey = `Auth:${userId}:${platform}`;
    try {
      const redis = this.redisService.getClient(RedisDatabase.GLOBAL);
      const v = await redis.get(redisKey);
      if (!v) {
        throw new UnauthorizedException('未找到登录态');
      }
      const redisObj: any = (() => {
        try {
          return JSON.parse(v);
        } catch {
          throw new UnauthorizedException('登录态数据异常');
        }
      })();
      const redisToken = redisObj?.Token || redisObj?.token;
      // 因为会有同一个账号同平台登录多个（监控端和教师端使用同一个账号也都是web所以不做过多验证）
      if (!redisToken) {
        throw new UnauthorizedException('登录态已失效');
      }
    } catch (e) {
      if (e instanceof UnauthorizedException) {
        throw e;
      }
      this.logger.error(e, 'AuthGuard');
      throw new UnauthorizedException('鉴权失败');
    }
    (req as any).user = { userId, role };
    return true;
  }
}
