/**
 * Event-driven credential issuance
 * Auto-issues credentials based on events (user registration, eIDAS verification, appointments, etc.)
 */

import { getEventBus, UserEvents, CredentialEvents } from '@the-order/events';
import {
  createVerifiableCredential,
  getCredentialTemplateByName,
  renderCredentialFromTemplate,
} from '@the-order/database';
import { KMSClient } from '@the-order/crypto';
import { getEnv } from '@the-order/shared';
import { randomUUID } from 'crypto';

export interface EventDrivenIssuanceConfig {
  kmsClient: KMSClient;
  autoIssueOnRegistration?: boolean;
  autoIssueOnEIDASVerification?: boolean;
  autoIssueOnAppointment?: boolean;
  autoIssueOnDocumentApproval?: boolean;
  autoIssueOnPaymentCompletion?: boolean;
}

/**
 * Initialize event-driven credential issuance
 */
export async function initializeEventDrivenIssuance(
  config: EventDrivenIssuanceConfig
): Promise<void> {
  const eventBus = getEventBus();
  const env = getEnv();
  const issuerDid = env.VC_ISSUER_DID || (env.VC_ISSUER_DOMAIN ? `did:web:${env.VC_ISSUER_DOMAIN}` : undefined);

  if (!issuerDid) {
    throw new Error('VC_ISSUER_DID or VC_ISSUER_DOMAIN must be configured for event-driven issuance');
  }

  // Subscribe to user registration events
  if (config.autoIssueOnRegistration) {
    await eventBus.subscribe(UserEvents.REGISTERED, async (data) => {
      const { userId, email, did } = data as { userId: string; email?: string; did?: string };
      await issueCredentialOnRegistration(userId, email, did || userId, issuerDid, config.kmsClient);
    });
  }

  // Subscribe to eIDAS verification events
  if (config.autoIssueOnEIDASVerification) {
    await eventBus.subscribe('eidas.verified', async (data) => {
      const { userId, did, eidasData } = data as {
        userId: string;
        did: string;
        eidasData: Record<string, unknown>;
      };
      await issueCredentialOnEIDASVerification(userId, did, eidasData, issuerDid, config.kmsClient);
    });
  }

  // Subscribe to appointment events
  if (config.autoIssueOnAppointment) {
    await eventBus.subscribe('appointment.created', async (data) => {
      const { userId, role, appointmentData } = data as {
        userId: string;
        role: string;
        appointmentData: Record<string, unknown>;
      };
      await issueCredentialOnAppointment(userId, role, appointmentData, issuerDid, config.kmsClient);
    });
  }

  // Subscribe to document approval events
  if (config.autoIssueOnDocumentApproval) {
    await eventBus.subscribe('document.approved', async (data) => {
      const { userId, documentId, documentType } = data as {
        userId: string;
        documentId: string;
        documentType: string;
      };
      await issueCredentialOnDocumentApproval(
        userId,
        documentId,
        documentType,
        issuerDid,
        config.kmsClient
      );
    });
  }

  // Subscribe to payment completion events
  if (config.autoIssueOnPaymentCompletion) {
    await eventBus.subscribe('payment.completed', async (data) => {
      const { userId, paymentId, amount, currency } = data as {
        userId: string;
        paymentId: string;
        amount: number;
        currency: string;
      };
      await issueCredentialOnPaymentCompletion(
        userId,
        paymentId,
        amount,
        currency,
        issuerDid,
        config.kmsClient
      );
    });
  }
}

/**
 * Issue credential on user registration
 */
async function issueCredentialOnRegistration(
  userId: string,
  email: string | undefined,
  subjectDid: string,
  issuerDid: string,
  kmsClient: KMSClient
): Promise<void> {
  try {
    // Try to get registration credential template
    const template = await getCredentialTemplateByName('user-registration');
    const credentialSubject = template
      ? renderCredentialFromTemplate(template, { userId, email, did: subjectDid })
      : {
          userId,
          email,
          registeredAt: new Date().toISOString(),
        };

    await issueCredential({
      subjectDid,
      issuerDid,
      credentialType: ['VerifiableCredential', 'IdentityCredential', 'RegistrationCredential'],
      credentialSubject,
      kmsClient,
    });

    const eventBus = getEventBus();
    await eventBus.publish(CredentialEvents.ISSUED, {
      subjectDid,
      credentialType: 'RegistrationCredential',
      issuedAt: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Failed to issue credential on registration:', error);
  }
}

/**
 * Issue credential on eIDAS verification
 */
async function issueCredentialOnEIDASVerification(
  userId: string,
  subjectDid: string,
  eidasData: Record<string, unknown>,
  issuerDid: string,
  kmsClient: KMSClient
): Promise<void> {
  try {
    const template = await getCredentialTemplateByName('eidas-verification');
    const credentialSubject = template
      ? renderCredentialFromTemplate(template, { userId, did: subjectDid, ...eidasData })
      : {
          userId,
          eidasVerified: true,
          eidasData,
          verifiedAt: new Date().toISOString(),
        };

    await issueCredential({
      subjectDid,
      issuerDid,
      credentialType: ['VerifiableCredential', 'IdentityCredential', 'EIDASVerificationCredential'],
      credentialSubject,
      kmsClient,
    });

    const eventBus = getEventBus();
    await eventBus.publish(CredentialEvents.ISSUED, {
      subjectDid,
      credentialType: 'EIDASVerificationCredential',
      issuedAt: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Failed to issue credential on eIDAS verification:', error);
  }
}

/**
 * Issue credential on appointment
 */
async function issueCredentialOnAppointment(
  userId: string,
  role: string,
  appointmentData: Record<string, unknown>,
  issuerDid: string,
  kmsClient: KMSClient
): Promise<void> {
  try {
    const template = await getCredentialTemplateByName(`appointment-${role.toLowerCase()}`);
    const credentialSubject = template
      ? renderCredentialFromTemplate(template, { userId, role, ...appointmentData })
      : {
          userId,
          role,
          appointmentData,
          appointedAt: new Date().toISOString(),
        };

    // Extract subjectDid from appointmentData or use userId
    const subjectDidValue = (appointmentData.subjectDid as string) || `did:key:${userId}`;
    
    await issueCredential({
      subjectDid: subjectDidValue,
      issuerDid,
      credentialType: ['VerifiableCredential', 'AppointmentCredential', `${role}Credential`],
      credentialSubject,
      kmsClient,
    });

    const eventBus = getEventBus();
    await eventBus.publish(CredentialEvents.ISSUED, {
      subjectDid: userId,
      credentialType: `${role}Credential`,
      issuedAt: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Failed to issue credential on appointment:', error);
  }
}

/**
 * Issue credential on document approval
 */
async function issueCredentialOnDocumentApproval(
  userId: string,
  documentId: string,
  documentType: string,
  issuerDid: string,
  kmsClient: KMSClient
): Promise<void> {
  try {
    const template = await getCredentialTemplateByName(`document-approval-${documentType.toLowerCase()}`);
    const credentialSubject = template
      ? renderCredentialFromTemplate(template, { userId, documentId, documentType })
      : {
          userId,
          documentId,
          documentType,
          approvedAt: new Date().toISOString(),
        };

    await issueCredential({
      subjectDid: userId,
      issuerDid,
      credentialType: ['VerifiableCredential', 'DocumentApprovalCredential', `${documentType}ApprovalCredential`],
      credentialSubject,
      kmsClient,
    });
  } catch (error) {
    console.error('Failed to issue credential on document approval:', error);
  }
}

/**
 * Issue credential on payment completion
 */
async function issueCredentialOnPaymentCompletion(
  userId: string,
  paymentId: string,
  amount: number,
  currency: string,
  issuerDid: string,
  kmsClient: KMSClient
): Promise<void> {
  try {
    const template = await getCredentialTemplateByName('payment-completion');
    const credentialSubject = template
      ? renderCredentialFromTemplate(template, { userId, paymentId, amount, currency })
      : {
          userId,
          paymentId,
          amount,
          currency,
          completedAt: new Date().toISOString(),
        };

    await issueCredential({
      subjectDid: userId,
      issuerDid,
      credentialType: ['VerifiableCredential', 'PaymentCredential'],
      credentialSubject,
      kmsClient,
    });
  } catch (error) {
    console.error('Failed to issue credential on payment completion:', error);
  }
}

/**
 * Helper to issue a credential
 */
async function issueCredential(params: {
  subjectDid: string;
  issuerDid: string;
  credentialType: string[];
  credentialSubject: Record<string, unknown>;
  kmsClient: KMSClient;
}): Promise<void> {
  const { subjectDid, issuerDid, credentialType, credentialSubject, kmsClient } = params;

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

  const credentialData = {
    id: credentialId,
    type: credentialType,
    issuer: issuerDid,
    subject: subjectDid,
    credentialSubject,
    issuanceDate: issuanceDate.toISOString(),
  };

  const credentialJson = JSON.stringify(credentialData);
  const signature = await kmsClient.sign(Buffer.from(credentialJson));

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

  await createVerifiableCredential({
    credential_id: credentialId,
    issuer_did: issuerDid,
    subject_did: subjectDid,
    credential_type: credentialType,
    credential_subject: credentialSubject,
    issuance_date: issuanceDate,
    expiration_date: undefined,
    proof,
  });
}

