/**
 * FederationController — request handlers for federation partner management and token verification.
 *
 * Handlers:
 *   POST   /federation/trust             → registerPartner
 *   GET    /federation/partners          → listPartners
 *   GET    /federation/partners/:id      → getPartner
 *   PATCH  /federation/partners/:id      → updatePartner
 *   DELETE /federation/partners/:id      → deletePartner
 *   POST   /federation/verify            → verifyToken
 */

import { Request, Response, NextFunction } from 'express';

import { FederationService } from '../services/FederationService.js';
import {
  ICreatePartnerRequest,
  IUpdatePartnerRequest,
  IFederationVerifyRequest,
} from '../types/federation.js';

/**
 * Controller for all federation-related endpoints.
 * Delegates all business logic to FederationService.
 */
export class FederationController {
  /**
   * @param federationService - Service that manages federation partners and token verification.
   */
  constructor(private readonly federationService: FederationService) {}

  // ─────────────────────────────────────────────────────────────────────────
  // Public handlers
  // ─────────────────────────────────────────────────────────────────────────

  /**
   * Registers a new trusted federation partner.
   *
   * Validates that the partner's JWKS endpoint is reachable before persisting.
   * Responds 201 with the created IFederationPartner on success.
   *
   * @param req - Express request. Body must conform to ICreatePartnerRequest.
   * @param res - Express response.
   * @param next - Express next function — forwards errors to the global error handler.
   */
  async registerPartner(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const body = req.body as ICreatePartnerRequest;
      const partner = await this.federationService.registerPartner(body);
      res.status(201).json(partner);
    } catch (err) {
      next(err);
    }
  }

  /**
   * Returns all registered federation partners.
   *
   * Partners past their expires_at are marked as 'expired' before the list is returned.
   * Responds 200 with an array of IFederationPartner objects.
   *
   * @param _req - Express request (unused).
   * @param res - Express response.
   * @param next - Express next function — forwards errors to the global error handler.
   */
  async listPartners(_req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const partners = await this.federationService.listPartners();
      res.status(200).json(partners);
    } catch (err) {
      next(err);
    }
  }

  /**
   * Returns a single federation partner by its UUID.
   *
   * Responds 200 with the matching IFederationPartner.
   * Responds 404 if the partner does not exist.
   *
   * @param req - Express request. `req.params.id` must be the partner UUID.
   * @param res - Express response.
   * @param next - Express next function — forwards errors to the global error handler.
   */
  async getPartner(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const partner = await this.federationService.getPartner(id);
      res.status(200).json(partner);
    } catch (err) {
      next(err);
    }
  }

  /**
   * Updates an existing federation partner.
   *
   * Only provided fields are updated. Responds 200 with the updated IFederationPartner.
   * Responds 404 if the partner does not exist.
   *
   * @param req - Express request. `req.params.id` is the partner UUID; body is IUpdatePartnerRequest.
   * @param res - Express response.
   * @param next - Express next function — forwards errors to the global error handler.
   */
  async updatePartner(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const body = req.body as IUpdatePartnerRequest;
      const partner = await this.federationService.updatePartner(id, body);
      res.status(200).json(partner);
    } catch (err) {
      next(err);
    }
  }

  /**
   * Deletes a federation partner by its UUID.
   *
   * Invalidates the partner's cached JWKS on success.
   * Responds 204 No Content on success.
   * Responds 404 if the partner does not exist.
   *
   * @param req - Express request. `req.params.id` must be the partner UUID.
   * @param res - Express response.
   * @param next - Express next function — forwards errors to the global error handler.
   */
  async deletePartner(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      await this.federationService.deletePartner(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }

  /**
   * Verifies a federated JWT token from a registered partner IdP.
   *
   * Performs full signature verification, expiry check, and organization filtering.
   * Responds 200 with an IFederationVerifyResult on success.
   * Responds 401 if the token fails any verification check.
   *
   * @param req - Express request. Body must contain `token: string` and optional `expected_issuer`.
   * @param res - Express response.
   * @param next - Express next function — forwards errors to the global error handler.
   */
  async verifyToken(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const body = req.body as IFederationVerifyRequest;
      const result = await this.federationService.verifyFederatedToken(body);
      res.status(200).json(result);
    } catch (err) {
      next(err);
    }
  }
}
