// Input Validation Middleware
// Uses Zod for request validation

import { Request, Response, NextFunction } from 'express';
import { z, ZodError } from 'zod';
import { DbisError, ErrorCode } from '@/shared/types';
import { logger } from '@/infrastructure/monitoring/logger';

/**
 * Validation middleware factory
 * Creates middleware that validates request body, query, or params
 */
export function validateRequest(schema: {
  body?: z.ZodSchema<any>;
  query?: z.ZodSchema<any>;
  params?: z.ZodSchema<any>;
}) {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      // Validate body
      if (schema.body) {
        req.body = await schema.body.parseAsync(req.body);
      }

      // Validate query
      if (schema.query) {
        req.query = await schema.query.parseAsync(req.query);
      }

      // Validate params
      if (schema.params) {
        req.params = await schema.params.parseAsync(req.params);
      }

      next();
    } catch (error) {
      if (error instanceof ZodError) {
        const errors = error.errors.map((err) => ({
          path: err.path.join('.'),
          message: err.message,
        }));

        logger.warn('Request validation failed', {
          path: req.path,
          method: req.method,
          errors,
        });

        next(
          new DbisError(
            ErrorCode.VALIDATION_ERROR,
            `Validation failed: ${errors.map((e) => e.message).join(', ')}`
          )
        );
        return;
      }

      next(error);
    }
  };
}

/**
 * Common validation schemas for IRU endpoints
 */
export const iruValidationSchemas = {
  // Marketplace
  inquiryRequest: z.object({
    offeringId: z.string().min(1, 'Offering ID is required'),
    organizationName: z.string().min(1, 'Organization name is required'),
    institutionalType: z.string().min(1, 'Institutional type is required'),
    jurisdiction: z.string().length(2, 'Jurisdiction must be a 2-letter country code'),
    contactEmail: z.string().email('Invalid email address'),
    contactPhone: z.string().optional(),
    contactName: z.string().min(1, 'Contact name is required'),
    estimatedVolume: z.string().optional(),
    expectedGoLive: z.string().datetime().optional().or(z.date().optional()),
  }),

  // Payment
  paymentRequest: z.object({
    subscriptionId: z.string().min(1, 'Subscription ID is required'),
    amount: z.number().positive('Amount must be positive'),
    currency: z.string().length(3, 'Currency must be a 3-letter code'),
    paymentMethod: z.enum(['stripe', 'braintree'], {
      errorMap: () => ({ message: 'Payment method must be stripe or braintree' }),
    }),
    paymentMethodId: z.string().min(1, 'Payment method ID is required'),
    description: z.string().optional(),
  }),

  // Deployment
  deploymentRequest: z.object({
    subscriptionId: z.string().min(1, 'Subscription ID is required'),
    deploymentConfig: z.record(z.any()).optional(),
  }),

  // Qualification
  qualificationRequest: z.object({
    inquiryId: z.string().min(1, 'Inquiry ID is required'),
    preliminaryInfo: z.record(z.any()).optional(),
  }),

  // Agreement
  agreementRequest: z.object({
    subscriptionId: z.string().min(1, 'Subscription ID is required'),
    templateId: z.string().optional(),
    variables: z.record(z.any()).optional(),
  }),

  // Notification
  notificationRequest: z.object({
    recipient: z.string().min(1, 'Recipient is required'),
    recipientType: z.enum(['email', 'sms', 'portal']),
    template: z.string().min(1, 'Template is required'),
    variables: z.record(z.any()),
    priority: z.enum(['low', 'normal', 'high', 'urgent']).optional(),
  }),

  // Common query params
  paginationQuery: z.object({
    limit: z.string().regex(/^\d+$/).transform(Number).pipe(z.number().min(1).max(100)).optional(),
    offset: z.string().regex(/^\d+$/).transform(Number).pipe(z.number().min(0)).optional(),
  }),

  // Common params
  idParam: z.object({
    id: z.string().min(1, 'ID is required'),
  }),
};

export const nostroVostroValidationSchemas = {
  participantCreateRequest: z.object({
    participantId: z.string().min(1, 'Participant ID is required').optional(),
    name: z.string().min(1, 'Institution name is required'),
    bic: z
      .string()
      .regex(/^[A-Za-z0-9]{8}([A-Za-z0-9]{3})?$/, 'BIC must be 8 or 11 alphanumeric characters')
      .optional(),
    lei: z
      .string()
      .trim()
      .min(20, 'LEI is required')
      .max(20, 'LEI must be exactly 20 characters'),
    country: z
      .string()
      .trim()
      .length(2, 'Country must be a 2-letter ISO code')
      .transform((value) => value.toUpperCase()),
    regulatoryTier: z.enum(['SCB', 'Tier1', 'Tier2', 'PSP']),
    sovereignBankId: z.string().min(1, 'Sovereign bank ID is required').optional(),
    metadata: z.record(z.any()).optional(),
  }),
};

export const authValidationSchemas = {
  loginRequest: z.object({
    username: z.string().min(1, 'Username is required'),
    password: z.string().min(1, 'Password is required'),
    otp: z
      .string()
      .trim()
      .regex(/^\d{6}$/, 'OTP must be a 6-digit code')
      .optional(),
    surface: z.enum(['admin', 'member', 'core']).optional(),
  }),
  passwordChangeRequest: z.object({
    currentPassword: z.string().min(1, 'Current password is required'),
    newPassword: z.string().min(1, 'New password is required'),
  }),
  passwordResetCompleteRequest: z.object({
    username: z.string().min(1, 'Username is required'),
    resetToken: z.string().min(1, 'Reset token is required'),
    newPassword: z.string().min(1, 'New password is required'),
    surface: z.enum(['admin', 'member', 'core']).optional(),
  }),
  passwordResetRequest: z.object({
    username: z.string().min(1, 'Username is required'),
    surface: z.enum(['admin', 'member', 'core']).optional(),
  }),
  mfaSetupEnableRequest: z.object({
    secret: z.string().min(16, 'MFA secret is required'),
    otp: z.string().trim().regex(/^\d{6}$/, 'OTP must be a 6-digit code'),
  }),
  mfaDisableRequest: z.object({
    otp: z.string().trim().regex(/^\d{6}$/, 'OTP must be a 6-digit code'),
  }),
  issueEmployeeAccountRequest: z.object({
    employeeId: z.string().min(1, 'Employee ID is required'),
    employeeName: z.string().min(1, 'Employee name is required'),
    email: z.string().email('Valid email is required'),
    roleName: z.string().min(1, 'Role name is required'),
    securityClearance: z.string().min(1, 'Security clearance is required'),
    password: z.string().min(1, 'Password is required'),
    mustRotatePassword: z.boolean().optional(),
  }),
  issueMemberAccountRequest: z.object({
    memberId: z.string().min(1, 'Member ID is required'),
    memberName: z.string().min(1, 'Member name is required'),
    email: z.string().email('Valid email is required'),
    institutionName: z.string().min(1, 'Institution name is required'),
    institutionCountry: z
      .string()
      .trim()
      .length(2, 'Institution country must be a 2-letter ISO code')
      .transform((value) => value.toUpperCase()),
    participantId: z.string().min(1, 'Participant ID is required').optional(),
    lei: z.string().trim().length(20, 'LEI must be exactly 20 characters').optional(),
    sovereignBankId: z.string().min(1, 'Sovereign bank ID is required').optional(),
    password: z.string().min(1, 'Password is required'),
    mustRotatePassword: z.boolean().optional(),
  }),
  issueResetTokenRequest: z.object({
    accountType: z.enum(['employee', 'member']),
    identifier: z.string().min(1, 'Identifier is required'),
  }),
  deactivateAccountRequest: z.object({
    accountType: z.enum(['employee', 'member']),
    identifier: z.string().min(1, 'Identifier is required'),
  }),
};
