import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import jwt from 'jsonwebtoken';
import type { ActorContext } from '../common/actor-context.js';
import { MESSAGES } from '../common/messages.js';
import { PrismaService } from '../prisma.service.js';

/**
 * AccessToken 守卫：校验 Bearer JWT 并把 actor 注入到 request 上下文。
 */
@Injectable()
export class AccessTokenGuard implements CanActivate {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * 守卫入口：认证通过返回 true，失败抛出 401。
   */
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest<{
      headers: Record<string, string | string[] | undefined>;
      actor?: unknown;
    }>();

    const authorization = request.headers.authorization;
    const headerValue = Array.isArray(authorization)
      ? authorization[0]
      : authorization;

    if (!headerValue || !headerValue.startsWith('Bearer ')) {
      throw new UnauthorizedException(MESSAGES.AUTH.MISSING_BEARER);
    }

    const token = headerValue.slice('Bearer '.length).trim();
    request.actor = await this.verifyAndExtractActor(token);

    return true;
  }

  /**
   * 解析并验证 token，同时回库确认当前用户仍然有效。
   */
  private async verifyAndExtractActor(token: string): Promise<ActorContext> {
    const secret = process.env.AUTH_TOKEN_SECRET;
    if (!secret) {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_SECRET_MISSING);
    }

    let payload: string | jwt.JwtPayload;
    try {
      payload = jwt.verify(token, secret, {
        algorithms: ['HS256'],
        issuer: 'tyt-api-nest',
      });
    } catch {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_INVALID);
    }

    if (typeof payload !== 'object') {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_PAYLOAD_INVALID);
    }

    const userId = this.asInt(payload.id, 'id');
    const issuedAt = this.asInt(payload.iat, 'iat');
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        role: true,
        hospitalId: true,
        departmentId: true,
        groupId: true,
        tokenValidAfter: true,
      },
    });

    // 数据库里已经没有该用户时，旧 token 必须立即失效。
    if (!user) {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_USER_NOT_FOUND);
    }

    // JWT 的 iat 精度是秒，这里按秒比较，避免同秒登录被误伤。
    const tokenValidAfterUnix = Math.floor(
      user.tokenValidAfter.getTime() / 1000,
    );
    if (issuedAt < tokenValidAfterUnix) {
      throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_REVOKED);
    }

    return {
      id: user.id,
      role: user.role,
      hospitalId: user.hospitalId,
      departmentId: user.departmentId,
      groupId: user.groupId,
    };
  }

  /**
   * 严格校验 token 中必须为整数的字段。
   */
  private asInt(value: unknown, field: string): number {
    if (typeof value !== 'number' || !Number.isInteger(value)) {
      throw new UnauthorizedException(
        `${MESSAGES.AUTH.TOKEN_FIELD_INVALID}: ${field}`,
      );
    }
    return value;
  }
}
