import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { Request } from 'express';
import { AuthService } from '../auth.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  private readonly logger = new Logger(JwtStrategy.name);

  constructor(
    private configService: ConfigService,
    private authService: AuthService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get<string>('jwt.secret') || 'default-secret-key',
      // 让 validate 拿到 Request，从 X-Organization-Id header 提取当前组织
      // 传给 validateUser，使 sliceAuthPayload 切出当前组织的权限切片（issue #160）
      passReqToCallback: true,
    });
  }

  async validate(req: Request, payload: any) {
    // 规则 §2.2 + §5.3.5：JTI 在黑名单 → 拒绝；
    // Redis 故障时 fail secure（拒绝认证），不冒泡 500，不静默放行
    //
    // 黑名单检查 + validateUser 完全独立，并发执行减少每请求 1 次串行 RTT
    //
    // X-Organization-Id：前端 OrganizationContext 切组织时由 api-client 注入；
    // 缺失时 currentOrganizationId 为 undefined，validateUser 退回到 region 兜底
    // 分支（即当前 v2.1 行为）。这一硬化为 v2.2 移除 region 兜底铺路。
    const orgHeader = req.headers['x-organization-id'];
    const currentOrganizationId = Array.isArray(orgHeader)
      ? orgHeader[0]
      : orgHeader;

    const [blacklistResult, user] = await Promise.all([
      payload?.jti
        ? this.authService
            .isJtiBlacklisted(payload.jti)
            .then((blacklisted) => ({ ok: true as const, blacklisted }))
            .catch((err: unknown) => ({
              ok: false as const,
              err: err instanceof Error ? err.message : String(err),
            }))
        : Promise.resolve({ ok: true as const, blacklisted: false }),
      this.authService.validateUser(payload.sub, currentOrganizationId),
    ]);

    if (!blacklistResult.ok) {
      this.logger.warn(`黑名单检查失败（fail secure）: ${blacklistResult.err}`);
      throw new UnauthorizedException('Token validation unavailable');
    }
    if (blacklistResult.blacklisted) {
      throw new UnauthorizedException('Token has been revoked');
    }
    if (!user) {
      throw new UnauthorizedException();
    }

    return { ...user, jti: payload.jti };
  }
}
