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

/**
 * 从 JWT 取 host/tenant/orgScope,展开成 TenantScopeContext 挂到 request,
 * handler/service 经 @TenantScope() 取用,无需重读 token。
 *
 * 设计 B:JWT 装通用 orgScope,这里按 host org 树(从摄入数据派生)展开成内部过滤维。
 * 展开需查/缓存树 → 异步;用 from()+mergeMap() 串进 Observable,ALS 仍在 handler 执行处生效。
 *
 * 不臆造 scope:未鉴权(public 路由)原样放行。
 */
@Injectable()
export class TenantScopeInterceptor implements NestInterceptor {
  constructor(private readonly orgTree: OrgTreeService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const req = context.switchToHttp().getRequest<{
      user?: AccessTokenPayload;
      tenantScope?: TenantScopeContext;
    }>();
    const user = req.user;
    if (!user) return next.handle();

    return from(this.orgTree.expandScope(user.hostId, user.tenantId, user.orgScope ?? [])).pipe(
      mergeMap(({ sourceUnits, clinicIds }) => {
        req.tenantScope = {
          hostId: user.hostId,
          tenantId: user.tenantId,
          clinicIds,
          sourceUnits,
          userId: user.sub,
        };
        // 纵深防御:把租户上下文存进 ALS,让 Prisma 扩展对漏写 tenantId 的读查询自动兜底。
        //   subscribe 在 als.run 内触发 → handler 的异步链(await prisma…)继承该上下文。
        const alsCtx = { hostId: user.hostId, tenantId: user.tenantId, sourceUnits };
        return new Observable((subscriber) => {
          tenantAls.run(alsCtx, () => {
            next.handle().subscribe({
              next: (v) => subscriber.next(v),
              error: (e) => subscriber.error(e),
              complete: () => subscriber.complete(),
            });
          });
        });
      }),
    );
  }
}
