import {
  BadRequestException,
  Controller,
  ForbiddenException,
  Get,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import { AdminJwtAuthGuard } from '../auth/admin/jwt.guard';
import { RolesGuard } from '../auth/admin/roles.guard';
import { Roles } from '../auth/admin/roles.decorator';
import {
  CorpAnalysisView,
  CorpAnalysisViewGuard,
} from '../auth/admin/corp-analysis-view.guard';
import { AnalyzeRepository } from '../analyze/analyze.repository';
import { AdminViewTokenService } from './admin-view-token.service';
import { SuperAdminService } from '../admin/super-admin.service';
import { canViewCarFor } from '../auth/admin/car-view-admin.guard';

const VALID_SCOPES = ['analyze', 'b2c-analyze', 'client-analyze'];

/**
 * ★보안(C2): admin(AdminJwtAuthGuard 보호)이 특정 분석건(type+id)에 스코프된 단명 토큰을 발급받는 엔드포인트.
 *   admin 팝업이 이 토큰을 iframe URL ?t= 로 실어 site admin-view 를 띄우면, site→server admin-view 호출이
 *   토큰으로 인가된다. 내부 관리자 · 기업 관리자 · 영업(RolesGuard)이 발급받는다. 기업 관리자는 기업 레벨
 *   (CorpAnalysisViewGuard)과 분석건 범위 검사를, 영업은 담당 범위(isOddInSalesScope) 검사를 더 거친다.
 *   그래서 순차 id 순회 IDOR가 막힌다.
 */
@Controller('admin-view-token')
@UseGuards(AdminJwtAuthGuard, RolesGuard, CorpAnalysisViewGuard)
//* 역할 없는 신원은 발급할 수 없다(K5) — 역할 판정은 RolesGuard, 기업 레벨 판정은 CorpAnalysisViewGuard 가 따로 건다
//* 영업은 담당 영업대리점 범위(isOddInSalesScope) 안의 분석 건만 발급받는다 — 밖이면 403
@Roles('admin', 'corp', 'sales')
//* corp는 분석 상세팝업(레벨2) 허용 기업만 admin-view 토큰 발급 가능
@CorpAnalysisView(2)
export class AdminViewTokenController {
  constructor(
    private tokenService: AdminViewTokenService,
    private analyzeRepository: AnalyzeRepository,
    private superAdmin: SuperAdminService,
  ) {}

  @Get()
  async issue(
    @Query('type') type: string,
    @Query('id') id: string,
    @Req() req: Request,
  ) {
    if (!VALID_SCOPES.includes(type)) {
      throw new BadRequestException('invalid type');
    }
    const numId = Number(id);
    if (!Number.isInteger(numId) || numId <= 0) {
      throw new BadRequestException('invalid id');
    }
    await this.assertViewScope(type, numId, req['admin']);
    const admin = req['admin'];
    return {
      token: this.tokenService.issue(
        type,
        numId,
        admin,
        await canViewCarFor(this.superAdmin, admin),
      ),
    };
  }

  private async assertViewScope(type: string, id: number, admin: any) {
    if (admin?.role === 'sales') {
      const inScope =
        type === 'analyze' &&
        (await this.analyzeRepository.isOddInSalesScope(id, admin.sagId));
      if (!inScope) {
        throw new ForbiddenException('접근 권한이 없습니다');
      }
      return;
    }
    if (admin?.role !== 'corp') return;

    if (type !== 'analyze') {
      throw new ForbiddenException('접근 권한이 없습니다');
    }

    const inScope = await this.analyzeRepository.isOddInCorpScope(
      id,
      admin.agcId,
      admin.corpScope === 'ORG' ? admin.orgPath : undefined,
    );
    if (!inScope) {
      throw new ForbiddenException('접근 권한이 없습니다');
    }
  }
}
