/**
 * Automated credential verification workflow
 * Auto-verify on receipt, verification receipt issuance, chain tracking, revocation checking
 */

import { getEventBus, CredentialEvents } from '@the-order/events';
import {
  getVerifiableCredentialById,
  isCredentialRevoked,
  createVerifiableCredential,
  logCredentialAction,
} from '@the-order/database';
import { KMSClient } from '@the-order/crypto';
import { DIDResolver } from '@the-order/auth';
import { getEnv } from '@the-order/shared';
import { randomUUID } from 'crypto';

export interface VerificationResult {
  valid: boolean;
  credentialId: string;
  verifiedAt: Date;
  verificationReceiptId?: string;
  errors?: string[];
  warnings?: string[];
}

/**
 * Initialize automated credential verification
 */
export async function initializeAutomatedVerification(kmsClient: KMSClient): Promise<void> {
  const eventBus = getEventBus();

  // Subscribe to credential received events
  await eventBus.subscribe('credential.received', async (data) => {
    const eventData = data as {
      credentialId: string;
      receivedBy: string;
      source?: string;
    };

    try {
      const result = await verifyCredential(eventData.credentialId, kmsClient);

      // Publish verification event
      await eventBus.publish(CredentialEvents.VERIFIED, {
        credentialId: eventData.credentialId,
        valid: result.valid,
        verifiedAt: result.verifiedAt.toISOString(),
        verificationReceiptId: result.verificationReceiptId,
        errors: result.errors,
        warnings: result.warnings,
      });

      // Issue verification receipt if valid
      if (result.valid && result.verificationReceiptId) {
        await eventBus.publish(CredentialEvents.ISSUED, {
          subjectDid: eventData.receivedBy,
          credentialType: ['VerifiableCredential', 'VerificationReceipt'],
          credentialId: result.verificationReceiptId,
          issuedAt: result.verifiedAt.toISOString(),
        });
      }
    } catch (error) {
      console.error('Failed to verify credential:', error);
    }
  });
}

/**
 * Verify a credential
 */
export async function verifyCredential(
  credentialId: string,
  kmsClient: KMSClient
): Promise<VerificationResult> {
  const errors: string[] = [];
  const warnings: string[] = [];

  // Get credential from database
  const credential = await getVerifiableCredentialById(credentialId);
  if (!credential) {
    return {
      valid: false,
      credentialId,
      verifiedAt: new Date(),
      errors: ['Credential not found'],
    };
  }

  // Check if revoked
  const revoked = await isCredentialRevoked(credentialId);
  if (revoked) {
    return {
      valid: false,
      credentialId,
      verifiedAt: new Date(),
      errors: ['Credential has been revoked'],
    };
  }

  // Check expiration
  if (credential.expiration_date && new Date() > credential.expiration_date) {
    return {
      valid: false,
      credentialId,
      verifiedAt: new Date(),
      errors: ['Credential has expired'],
    };
  }

  // Verify proof/signature
  try {
    const proof = credential.proof as {
      type?: string;
      verificationMethod?: string;
      jws?: string;
      created?: string;
    };

    if (!proof || !proof.jws) {
      errors.push('Credential missing proof');
    } else {
      // Verify signature using issuer DID
      const resolver = new DIDResolver();
      const credentialData = {
        id: credential.credential_id,
        type: credential.credential_type,
        issuer: credential.issuer_did,
        subject: credential.subject_did,
        credentialSubject: credential.credential_subject,
        issuanceDate: credential.issuance_date.toISOString(),
        expirationDate: credential.expiration_date?.toISOString(),
      };

      const credentialJson = JSON.stringify(credentialData);
      const isValid = await resolver.verifySignature(
        credential.issuer_did,
        credentialJson,
        proof.jws
      );

      if (!isValid) {
        errors.push('Credential signature verification failed');
      }
    }
  } catch (error) {
    errors.push(`Signature verification error: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }

  // Verify credential chain (if applicable)
  // This would check parent credentials, issuer credentials, etc.
  // For now, we'll just log a warning if chain verification is needed
  if (credential.credential_type.includes('ChainCredential')) {
    warnings.push('Credential chain verification not fully implemented');
  }

  const valid = errors.length === 0;

  // Create verification receipt if valid
  let verificationReceiptId: string | undefined;
  if (valid) {
    try {
      verificationReceiptId = await createVerificationReceipt(credentialId, credential.issuer_did, kmsClient);
    } catch (error) {
      warnings.push(`Failed to create verification receipt: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Log verification action
  await logCredentialAction({
    credential_id: credentialId,
    issuer_did: credential.issuer_did,
    subject_did: credential.subject_did,
    credential_type: credential.credential_type,
    action: 'verified',
    metadata: {
      valid,
      errors,
      warnings,
      verificationReceiptId,
    },
  });

  return {
    valid,
    credentialId,
    verifiedAt: new Date(),
    verificationReceiptId,
    errors: errors.length > 0 ? errors : undefined,
    warnings: warnings.length > 0 ? warnings : undefined,
  };
}

/**
 * Create verification receipt
 */
async function createVerificationReceipt(
  verifiedCredentialId: string,
  issuerDid: string,
  kmsClient: KMSClient
): Promise<string> {
  const env = getEnv();
  const receiptIssuerDid = env.VC_ISSUER_DID || (env.VC_ISSUER_DOMAIN ? `did:web:${env.VC_ISSUER_DOMAIN}` : undefined);

  if (!receiptIssuerDid) {
    throw new Error('VC_ISSUER_DID or VC_ISSUER_DOMAIN must be configured');
  }

  const receiptId = randomUUID();
  const issuanceDate = new Date();

  const receiptData = {
    id: receiptId,
    type: ['VerifiableCredential', 'VerificationReceipt'],
    issuer: receiptIssuerDid,
    subject: issuerDid,
    credentialSubject: {
      verifiedCredentialId,
      verifiedAt: issuanceDate.toISOString(),
      verificationStatus: 'valid',
    },
    issuanceDate: issuanceDate.toISOString(),
  };

  const receiptJson = JSON.stringify(receiptData);
  const signature = await kmsClient.sign(Buffer.from(receiptJson));

  const proof = {
    type: 'KmsSignature2024',
    created: issuanceDate.toISOString(),
    proofPurpose: 'assertionMethod',
    verificationMethod: `${receiptIssuerDid}#kms-key`,
    jws: signature.toString('base64'),
  };

  await createVerifiableCredential({
    credential_id: receiptId,
    issuer_did: receiptIssuerDid,
    subject_did: issuerDid,
    credential_type: receiptData.type,
    credential_subject: receiptData.credentialSubject,
    issuance_date: issuanceDate,
    expiration_date: undefined,
    proof,
  });

  return receiptId;
}

/**
 * Verify credential chain
 */
export async function verifyCredentialChain(credentialId: string): Promise<{
  valid: boolean;
  chain: Array<{ credentialId: string; valid: boolean }>;
  errors: string[];
}> {
  const chain: Array<{ credentialId: string; valid: boolean }> = [];
  const errors: string[] = [];

  // Get credential
  const credential = await getVerifiableCredentialById(credentialId);
  if (!credential) {
    return { valid: false, chain, errors: ['Credential not found'] };
  }

  // Verify this credential (requires KMS client - this is a placeholder)
  // In production, this should be passed as a parameter
  // For now, we'll create a minimal verification
  const credentialVerification = await getVerifiableCredentialById(credentialId);
  const isValid = credentialVerification !== null && !credentialVerification.revoked;
  chain.push({ credentialId, valid: isValid });
  
  const verification = {
    valid: isValid,
    credentialId,
    verifiedAt: new Date(),
    errors: isValid ? undefined : ['Credential not found or revoked'],
  };

  if (!verification.valid && verification.errors) {
    errors.push(...verification.errors);
  }

  // In production, this would recursively verify parent credentials
  // For now, we'll just verify the immediate credential

  return {
    valid: chain.every((c) => c.valid),
    chain,
    errors,
  };
}

