/**
 * Authentication and authorization middleware
 */

import { FastifyRequest, FastifyReply } from 'fastify';
import { verify } from 'jsonwebtoken';
import { DIDResolver } from '@the-order/auth';
import { getEnv } from './env';
import { AppError } from './error-handler';
import fetch from 'node-fetch';

export interface AuthUser {
  id: string;
  email?: string;
  did?: string;
  roles?: string[];
}

declare module 'fastify' {
  interface FastifyRequest {
    user?: AuthUser;
  }
}

/**
 * JWT authentication middleware
 */
export async function authenticateJWT(
  request: FastifyRequest,
  _reply: FastifyReply
): Promise<void> {
  const authHeader = request.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new AppError(401, 'UNAUTHORIZED', 'Missing or invalid authorization header');
  }

  const token = authHeader.substring(7);
  const env = getEnv();

  if (!env.JWT_SECRET) {
    throw new AppError(500, 'CONFIG_ERROR', 'JWT secret not configured');
  }

  try {
    const decoded = verify(token, env.JWT_SECRET) as AuthUser;
    request.user = decoded;
  } catch (error) {
    throw new AppError(401, 'INVALID_TOKEN', 'Invalid or expired token');
  }
}

/**
 * DID-based authentication middleware
 */
export async function authenticateDID(
  request: FastifyRequest,
  _reply: FastifyReply
): Promise<void> {
  const didHeader = request.headers['x-did'] as string;
  const signatureHeader = request.headers['x-did-signature'] as string;
  const messageHeader = request.headers['x-did-message'] as string;

  if (!didHeader || !signatureHeader || !messageHeader) {
    throw new AppError(401, 'UNAUTHORIZED', 'Missing DID authentication headers');
  }

  try {
    const resolver = new DIDResolver();
    const isValid = await resolver.verifySignature(didHeader, messageHeader, signatureHeader);

    if (!isValid) {
      throw new AppError(401, 'INVALID_SIGNATURE', 'Invalid DID signature');
    }

    request.user = {
      id: didHeader,
      did: didHeader,
    };
  } catch (error) {
    if (error instanceof AppError) {
      throw error;
    }
    throw new AppError(401, 'AUTH_ERROR', 'DID authentication failed');
  }
}

/**
 * Role-based access control middleware
 */
export function requireRole(...allowedRoles: string[]) {
  return async (request: FastifyRequest, _reply: FastifyReply): Promise<void> => {
    if (!request.user) {
      throw new AppError(401, 'UNAUTHORIZED', 'Authentication required');
    }

    const userRoles = request.user.roles || [];
    const hasRole = allowedRoles.some((role) => userRoles.includes(role));

    if (!hasRole) {
      throw new AppError(403, 'FORBIDDEN', `Required role: ${allowedRoles.join(' or ')}`);
    }
  };
}

/**
 * OIDC token validation middleware
 */
export async function authenticateOIDC(
  request: FastifyRequest,
  _reply: FastifyReply
): Promise<void> {
  const authHeader = request.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new AppError(401, 'UNAUTHORIZED', 'Missing authorization header');
  }

  const token = authHeader.substring(7);
  const env = getEnv();

  // Validate token with OIDC issuer
  if (!env.OIDC_ISSUER) {
    throw new AppError(500, 'CONFIG_ERROR', 'OIDC issuer not configured');
  }

  try {
    // Introspect token with issuer
    const introspectionUrl = `${env.OIDC_ISSUER}/introspect`;
    const response = await fetch(introspectionUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Authorization: `Basic ${Buffer.from(`${env.OIDC_CLIENT_ID}:${env.OIDC_CLIENT_SECRET}`).toString('base64')}`,
      },
      body: new URLSearchParams({
        token,
        token_type_hint: 'access_token',
      }),
    });

    if (!response.ok) {
      throw new AppError(401, 'INVALID_TOKEN', 'Token introspection failed');
    }

    const tokenInfo = (await response.json()) as { active: boolean; sub?: string; email?: string };
    
    if (!tokenInfo.active) {
      throw new AppError(401, 'INVALID_TOKEN', 'Token is not active');
    }

    // Get user info from userinfo endpoint
    const userInfoUrl = `${env.OIDC_ISSUER}/userinfo`;
    const userInfoResponse = await fetch(userInfoUrl, {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    if (userInfoResponse.ok) {
      const userInfo = (await userInfoResponse.json()) as { sub: string; email?: string; name?: string };
      request.user = {
        id: userInfo.sub,
        email: userInfo.email,
      };
    } else {
      // Fallback to token info
      request.user = {
        id: tokenInfo.sub || 'oidc-user',
        email: tokenInfo.email,
      };
    }
  } catch (error) {
    if (error instanceof AppError) {
      throw error;
    }
    throw new AppError(401, 'AUTH_ERROR', 'OIDC token validation failed');
  }
}

