/**
 * Federation get verb (FED-M3-06).
 *
 * POST /api/federation/v1/get/:resource/:id
 *
 * Pipeline: FederationAuthGuard attaches the active grant context, then
 * FederationScopeService enforces grant scope + native RBAC intersection, then
 * the read-only query layer fetches one local row and tags it with `_source`.
 * Read audit-log writes are deferred to M4; this controller does not persist
 * request or response bodies.
 */

import { Controller, HttpException, Inject, Param, Post, Req, UseGuards } from '@nestjs/common';
import type { FastifyRequest } from 'fastify';
import {
  FederationInvalidRequestError,
  FederationNotFoundError,
  FederationScopeViolationError,
  FederationUnauthorizedError,
  SOURCE_LOCAL,
  type FederationGetResponse,
  type SourceTag,
} from '@mosaicstack/types';
import { FederationAuthGuard } from '../federation-auth.guard.js';
import '../federation-context.js';
import { FederationScopeService } from '../scope.service.js';
import { FederationGetQueryService } from './get-query.service.js';

type FederatedRow = Record<string, unknown> & SourceTag;

function scopeDenyToHttpException(deny: {
  readonly statusCode: 400 | 403;
  readonly message: string;
}): HttpException {
  const ErrorClass =
    deny.statusCode === 400 ? FederationInvalidRequestError : FederationScopeViolationError;
  return new HttpException(new ErrorClass(deny.message, deny).toEnvelope(), deny.statusCode);
}

@Controller('api/federation/v1/get')
@UseGuards(FederationAuthGuard)
export class GetController {
  constructor(
    @Inject(FederationScopeService) private readonly scope: FederationScopeService,
    @Inject(FederationGetQueryService) private readonly query: FederationGetQueryService,
  ) {}

  @Post(':resource/:id')
  async get(
    @Param('resource') resource: string,
    @Param('id') id: string,
    @Req() request: FastifyRequest,
  ): Promise<FederationGetResponse<FederatedRow>> {
    if (!request.federationContext) {
      throw new HttpException(
        new FederationUnauthorizedError('Federation context missing').toEnvelope(),
        401,
      );
    }
    if (id.trim().length === 0) {
      throw new HttpException(
        new FederationInvalidRequestError('Federation get id must not be empty').toEnvelope(),
        400,
      );
    }

    const scopeResult = await this.scope.evaluateAccess({
      context: request.federationContext,
      resource,
      requestedLimit: 1,
      nativeRbac: this.query,
    });

    if (!scopeResult.allowed) {
      throw scopeDenyToHttpException(scopeResult.deny);
    }

    const result = await this.query.get({ filter: scopeResult.filter, id });
    if (result.status === 'not_found') {
      throw new HttpException(
        new FederationNotFoundError('Requested federation resource was not found').toEnvelope(),
        404,
      );
    }
    if (result.status === 'denied') {
      throw new HttpException(
        new FederationScopeViolationError(result.reason, {
          resource,
          id,
          grantId: request.federationContext.grantId,
          peerId: request.federationContext.peerId,
          subjectUserId: request.federationContext.subjectUserId,
        }).toEnvelope(),
        403,
      );
    }

    return { item: { ...result.item, _source: SOURCE_LOCAL } };
  }
}
