/**
 * DSB Verifier SDK
 * JavaScript/TypeScript SDK for verifying eResidency and eCitizenship credentials
 */

import { eResidentCredentialSchema, eCitizenCredentialSchema, type VerifiablePresentation } from '@the-order/schemas';
import { DIDResolver } from '@the-order/auth';

export interface VerifierConfig {
  issuerDid: string;
  schemaRegistryUrl?: string;
  statusListUrl?: string;
}

export interface VerificationResult {
  valid: boolean;
  credentialId: string;
  credentialType: string[];
  verifiedAt: string;
  errors?: string[];
  warnings?: string[];
  status?: 'active' | 'suspended' | 'revoked' | 'expired';
}

/**
 * DSB Verifier
 */
export class DSBVerifier {
  private resolver: DIDResolver;
  private config: VerifierConfig;

  constructor(config: VerifierConfig) {
    this.config = config;
    this.resolver = new DIDResolver();
  }

  /**
   * Verify eResident credential
   */
  async verifyEResidentCredential(credential: unknown): Promise<VerificationResult> {
    const errors: string[] = [];
    const warnings: string[] = [];

    try {
      // Validate schema
      const validated = eResidentCredentialSchema.parse(credential);
      const credentialId = (validated as any).id || 'unknown';

      // Verify issuer
      if (validated.issuer !== this.config.issuerDid) {
        errors.push(`Invalid issuer: expected ${this.config.issuerDid}, got ${validated.issuer}`);
      }

      // Verify proof
      if (validated.proof) {
        const proofValid = await this.verifyProof(validated.issuer, validated, validated.proof);
        if (!proofValid) {
          errors.push('Proof verification failed');
        }
      } else {
        errors.push('Credential missing proof');
      }

      // Check expiration
      if (validated.expirationDate) {
        const expirationDate = new Date(validated.expirationDate);
        if (expirationDate < new Date()) {
          errors.push('Credential has expired');
        }
      }

      // Check credential status
      if (validated.credentialStatus) {
        const status = await this.checkCredentialStatus(validated.credentialStatus.id);
        if (status !== 'active') {
          errors.push(`Credential status: ${status}`);
        }
      }

      // Verify evidence
      if (validated.evidence && validated.evidence.length > 0) {
        for (const evidence of validated.evidence) {
          if (evidence.result === 'fail') {
            warnings.push(`Evidence check failed: ${evidence.type}`);
          }
        }
      }

      return {
        valid: errors.length === 0,
        credentialId,
        credentialType: validated.type,
        verifiedAt: new Date().toISOString(),
        errors: errors.length > 0 ? errors : undefined,
        warnings: warnings.length > 0 ? warnings : undefined,
        status: errors.length === 0 ? 'active' : undefined,
      };
    } catch (error) {
      return {
        valid: false,
        credentialId: 'unknown',
        credentialType: [],
        verifiedAt: new Date().toISOString(),
        errors: [error instanceof Error ? error.message : 'Unknown error'],
      };
    }
  }

  /**
   * Verify eCitizen credential
   */
  async verifyECitizenCredential(credential: unknown): Promise<VerificationResult> {
    const errors: string[] = [];
    const warnings: string[] = [];

    try {
      // Validate schema
      const validated = eCitizenCredentialSchema.parse(credential);
      const credentialId = (validated as any).id || 'unknown';

      // Verify issuer
      if (validated.issuer !== this.config.issuerDid) {
        errors.push(`Invalid issuer: expected ${this.config.issuerDid}, got ${validated.issuer}`);
      }

      // Verify proof
      if (validated.proof) {
        const proofValid = await this.verifyProof(validated.issuer, validated, validated.proof);
        if (!proofValid) {
          errors.push('Proof verification failed');
        }
      } else {
        errors.push('Credential missing proof');
      }

      // Check expiration
      if (validated.expirationDate) {
        const expirationDate = new Date(validated.expirationDate);
        if (expirationDate < new Date()) {
          errors.push('Credential has expired');
        }
      }

      // Check credential status
      if (validated.credentialStatus) {
        const status = await this.checkCredentialStatus(validated.credentialStatus.id);
        if (status !== 'active') {
          errors.push(`Credential status: ${status}`);
        }
      }

      // Verify evidence
      if (validated.evidence && validated.evidence.length > 0) {
        for (const evidence of validated.evidence) {
          if (evidence.result === 'fail') {
            warnings.push(`Evidence check failed: ${evidence.type}`);
          }
        }
      }

      // Verify oath date
      if (validated.credentialSubject.oathDate) {
        const oathDate = new Date(validated.credentialSubject.oathDate);
        if (oathDate > new Date()) {
          warnings.push('Oath date is in the future');
        }
      }

      return {
        valid: errors.length === 0,
        credentialId,
        credentialType: validated.type,
        verifiedAt: new Date().toISOString(),
        errors: errors.length > 0 ? errors : undefined,
        warnings: warnings.length > 0 ? warnings : undefined,
        status: errors.length === 0 ? 'active' : undefined,
      };
    } catch (error) {
      return {
        valid: false,
        credentialId: 'unknown',
        credentialType: [],
        verifiedAt: new Date().toISOString(),
        errors: [error instanceof Error ? error.message : 'Unknown error'],
      };
    }
  }

  /**
   * Verify verifiable presentation
   */
  async verifyPresentation(presentation: VerifiablePresentation, challenge?: string): Promise<VerificationResult> {
    const errors: string[] = [];
    const warnings: string[] = [];

    try {
      // Verify challenge if provided
      if (challenge && presentation.proof.challenge !== challenge) {
        errors.push('Challenge mismatch');
      }

      // Verify holder
      if (!presentation.holder) {
        errors.push('Presentation missing holder');
      }

      // Verify proof
      if (presentation.proof) {
        const proofValid = await this.verifyProof(presentation.holder, presentation, presentation.proof);
        if (!proofValid) {
          errors.push('Presentation proof verification failed');
        }
      } else {
        errors.push('Presentation missing proof');
      }

      // Verify all credentials in presentation
      const credentialResults = await Promise.all(
        presentation.verifiableCredential.map((cred) => {
          if (cred.type.includes('eResidentCredential')) {
            return this.verifyEResidentCredential(cred);
          } else if (cred.type.includes('eCitizenCredential')) {
            return this.verifyECitizenCredential(cred);
          } else {
            return Promise.resolve({
              valid: false,
              credentialId: 'unknown',
              credentialType: [],
              verifiedAt: new Date().toISOString(),
              errors: ['Unknown credential type'],
            });
          }
        })
      );

      const allValid = credentialResults.every((result) => result.valid);
      if (!allValid) {
        errors.push('One or more credentials failed verification');
        credentialResults.forEach((result) => {
          if (result.errors) {
            errors.push(...result.errors);
          }
        });
      }

      const firstCredential = presentation.verifiableCredential[0];
      const credentialId: string = firstCredential && 'id' in firstCredential && typeof firstCredential.id === 'string' 
        ? firstCredential.id 
        : 'unknown';
      
      return {
        valid: errors.length === 0 && allValid,
        credentialId,
        credentialType: presentation.verifiableCredential.flatMap((cred) => cred.type),
        verifiedAt: new Date().toISOString(),
        errors: errors.length > 0 ? errors : undefined,
        warnings: warnings.length > 0 ? warnings : undefined,
        status: errors.length === 0 && allValid ? 'active' : undefined,
      };
    } catch (error) {
      return {
        valid: false,
        credentialId: 'unknown',
        credentialType: [],
        verifiedAt: new Date().toISOString(),
        errors: [error instanceof Error ? error.message : 'Unknown error'],
      };
    }
  }

  /**
   * Verify proof
   */
  private async verifyProof(
    did: string,
    document: unknown,
    proof: {
      type: string;
      created: string;
      proofPurpose: string;
      verificationMethod: string;
      jws?: string;
    }
  ): Promise<boolean> {
    try {
      if (!proof.jws) {
        return false;
      }

      // Create message to verify (credential without proof)
      const documentObj = document as Record<string, unknown>;
      const documentWithoutProof: Record<string, unknown> = {};
      for (const key in documentObj) {
        if (key !== 'proof') {
          documentWithoutProof[key] = documentObj[key];
        }
      }
      const message = JSON.stringify(documentWithoutProof);

      // Verify signature using DID resolver
      const isValid = await this.resolver.verifySignature(did, message, proof.jws);
      return isValid;
    } catch (error) {
      console.error('Proof verification error:', error);
      return false;
    }
  }

  /**
   * Check credential status
   */
  private async checkCredentialStatus(_statusListId: string): Promise<'active' | 'suspended' | 'revoked' | 'expired'> {
    // TODO: Implement status list checking
    // const response = await fetch(`${this.config.statusListUrl}/${_statusListId}`);
    // const statusList = await response.json();
    // return statusList.status;

    return 'active'; // Placeholder
  }
}

/**
 * Create verifier instance
 */
export function createVerifier(config: VerifierConfig): DSBVerifier {
  return new DSBVerifier(config);
}

