import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import type { AccessTokenPayload } from '@pac/types';
import type { TenantScopeContext } from '../decorators/tenant-scope.decorator';
import { tenantAls } from '../tenant-context';

/**
 * Pulls host_id / tenant_id / clinic_ids out of the JWT payload and attaches
 * a normalized TenantScopeContext to the request, so handlers and services can
 * grab it via the @TenantScope() decorator without re-reading the token.
 *
 * The interceptor never invents a scope: if the request is unauthenticated
 * (public routes), it leaves the request untouched.
 */
@Injectable()
export class TenantScopeInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const req = context.switchToHttp().getRequest<{
      user?: AccessTokenPayload;
      tenantScope?: TenantScopeContext;
    }>();
    if (req.user) {
      req.tenantScope = {
        hostId: req.user.hostId,
        tenantId: req.user.tenantId,
        clinicIds: req.user.clinicIds ?? [],
        sourceUnits: req.user.sourceUnits ?? [],
        userId: req.user.sub,
      };
      // 纵深防御:把租户上下文存进 ALS,让 Prisma 扩展对漏写 tenantId 的读查询自动兜底。
      //   subscribe 在 als.run 内触发 → handler 的异步链(await prisma…)继承该上下文。
      const alsCtx = {
        hostId: req.user.hostId,
        tenantId: req.user.tenantId,
        sourceUnits: req.user.sourceUnits ?? [],
      };
      return new Observable((subscriber) => {
        tenantAls.run(alsCtx, () => {
          next.handle().subscribe({
            next: (v) => subscriber.next(v),
            error: (e) => subscriber.error(e),
            complete: () => subscriber.complete(),
          });
        });
      });
    }
    return next.handle();
  }
}
