/**
 * Authorization rules for credential issuance
 * Role-based issuance permissions, credential type restrictions, approval workflows
 */

import type { AuthUser } from './auth';

export interface CredentialIssuanceRule {
  credentialType: string | string[];
  allowedRoles: string[];
  requiresApproval?: boolean;
  approvalRoles?: string[];
  requiresMultiSignature?: boolean;
  minSignatures?: number;
  maxIssuancesPerUser?: number;
  maxIssuancesPerDay?: number;
  conditions?: (user: AuthUser, context: Record<string, unknown>) => Promise<boolean>;
}

export interface ApprovalRequest {
  credentialId: string;
  requestedBy: string;
  requestedAt: Date;
  credentialType: string[];
  subjectDid: string;
  status: 'pending' | 'approved' | 'rejected';
  approvals: Array<{
    approverId: string;
    approverRole: string;
    approvedAt: Date;
    signature?: string;
  }>;
  rejections: Array<{
    rejectorId: string;
    rejectorRole: string;
    rejectedAt: Date;
    reason: string;
  }>;
}

/**
 * Default authorization rules
 */
export const DEFAULT_ISSUANCE_RULES: CredentialIssuanceRule[] = [
  {
    credentialType: ['VerifiableCredential', 'IdentityCredential'],
    allowedRoles: ['admin', 'issuer', 'registrar'],
    requiresApproval: false,
  },
  {
    credentialType: ['VerifiableCredential', 'JudicialCredential', 'RegistrarCredential'],
    allowedRoles: ['admin', 'judicial-admin'],
    requiresApproval: true,
    approvalRoles: ['chief-judge', 'judicial-council'],
    requiresMultiSignature: true,
    minSignatures: 2,
  },
  {
    credentialType: ['VerifiableCredential', 'FinancialCredential', 'ComptrollerCredential'],
    allowedRoles: ['admin', 'financial-admin'],
    requiresApproval: true,
    approvalRoles: ['board', 'audit-committee'],
    requiresMultiSignature: true,
    minSignatures: 2,
  },
  {
    credentialType: ['VerifiableCredential', 'DiplomaticCredential', 'LettersOfCredence'],
    allowedRoles: ['admin', 'diplomatic-admin'],
    requiresApproval: true,
    approvalRoles: ['grand-master', 'sovereign-council'],
    requiresMultiSignature: true,
    minSignatures: 3,
  },
];

/**
 * Authorization service for credential issuance
 */
export class CredentialAuthorizationService {
  private rules: CredentialIssuanceRule[];

  constructor(rules: CredentialIssuanceRule[] = DEFAULT_ISSUANCE_RULES) {
    this.rules = rules;
  }

  /**
   * Check if user can issue a credential type
   */
  async canIssueCredential(
    user: AuthUser,
    credentialType: string | string[],
    context: Record<string, unknown> = {}
  ): Promise<{ allowed: boolean; requiresApproval: boolean; reason?: string }> {
    const credentialTypes = Array.isArray(credentialType) ? credentialType : [credentialType];

    // Find matching rule
    const rule = this.rules.find((r) => {
      const ruleTypes = Array.isArray(r.credentialType) ? r.credentialType : [r.credentialType];
      return credentialTypes.some((type) => ruleTypes.includes(type));
    });

    if (!rule) {
      // Default: only admins can issue unknown credential types
      if (!user.roles?.includes('admin')) {
        return {
          allowed: false,
          requiresApproval: true,
          reason: 'No authorization rule found for credential type',
        };
      }
      return { allowed: true, requiresApproval: false };
    }

    // Check role permissions
    const hasRole = user.roles?.some((role) => rule.allowedRoles.includes(role));
    if (!hasRole) {
      return {
        allowed: false,
        requiresApproval: false,
        reason: `User does not have required role. Required: ${rule.allowedRoles.join(' or ')}`,
      };
    }

    // Check custom conditions
    if (rule.conditions) {
      const conditionResult = await rule.conditions(user, context);
      if (!conditionResult) {
        return {
          allowed: false,
          requiresApproval: false,
          reason: 'Custom authorization condition failed',
        };
      }
    }

    return {
      allowed: true,
      requiresApproval: rule.requiresApproval || false,
    };
  }

  /**
   * Get approval requirements for credential type
   */
  getApprovalRequirements(credentialType: string | string[]): {
    requiresApproval: boolean;
    approvalRoles?: string[];
    requiresMultiSignature?: boolean;
    minSignatures?: number;
  } {
    const credentialTypes = Array.isArray(credentialType) ? credentialType : [credentialType];

    const rule = this.rules.find((r) => {
      const ruleTypes = Array.isArray(r.credentialType) ? r.credentialType : [r.credentialType];
      return credentialTypes.some((type) => ruleTypes.includes(type));
    });

    if (!rule || !rule.requiresApproval) {
      return { requiresApproval: false };
    }

    return {
      requiresApproval: true,
      approvalRoles: rule.approvalRoles,
      requiresMultiSignature: rule.requiresMultiSignature,
      minSignatures: rule.minSignatures,
    };
  }

  /**
   * Validate approval request
   */
  async validateApproval(
    approvalRequest: ApprovalRequest,
    approver: AuthUser
  ): Promise<{ valid: boolean; reason?: string }> {
    const requirements = this.getApprovalRequirements(approvalRequest.credentialType);

    if (!requirements.requiresApproval) {
      return { valid: false, reason: 'Credential type does not require approval' };
    }

    // Check if approver has required role
    if (requirements.approvalRoles) {
      const hasApprovalRole = approver.roles?.some((role) =>
        requirements.approvalRoles!.includes(role)
      );
      if (!hasApprovalRole) {
        return {
          valid: false,
          reason: `Approver does not have required role. Required: ${requirements.approvalRoles.join(' or ')}`,
        };
      }
    }

    // Check if already approved/rejected
    if (approvalRequest.status !== 'pending') {
      return { valid: false, reason: 'Approval request is not pending' };
    }

    // Check for duplicate approval
    const alreadyApproved = approvalRequest.approvals.some(
      (a) => a.approverId === approver.id
    );
    if (alreadyApproved) {
      return { valid: false, reason: 'Approver has already approved this request' };
    }

    return { valid: true };
  }

  /**
   * Check if approval request has sufficient approvals
   */
  hasSufficientApprovals(approvalRequest: ApprovalRequest): boolean {
    const requirements = this.getApprovalRequirements(approvalRequest.credentialType);

    if (!requirements.requiresApproval) {
      return true;
    }

    if (requirements.requiresMultiSignature && requirements.minSignatures) {
      return approvalRequest.approvals.length >= requirements.minSignatures;
    }

    return approvalRequest.approvals.length > 0;
  }

  /**
   * Add custom rule
   */
  addRule(rule: CredentialIssuanceRule): void {
    this.rules.push(rule);
  }

  /**
   * Remove rule
   */
  removeRule(credentialType: string | string[]): void {
    const credentialTypes = Array.isArray(credentialType) ? credentialType : [credentialType];
    this.rules = this.rules.filter((r) => {
      const ruleTypes = Array.isArray(r.credentialType) ? r.credentialType : [r.credentialType];
      return !credentialTypes.some((type) => ruleTypes.includes(type));
    });
  }
}

/**
 * Get default authorization service
 */
let defaultAuthService: CredentialAuthorizationService | null = null;

export function getAuthorizationService(): CredentialAuthorizationService {
  if (!defaultAuthService) {
    defaultAuthService = new CredentialAuthorizationService();
  }
  return defaultAuthService;
}

