/**
 * Identity Service
 * Handles eIDAS/DID, verifiable credentials, and identity management
 */

import Fastify from 'fastify';
import fastifySwagger from '@fastify/swagger';
import fastifySwaggerUI from '@fastify/swagger-ui';
import {
  errorHandler,
  createLogger,
  registerSecurityPlugins,
  addCorrelationId,
  addRequestLogging,
  getEnv,
  createBodySchema,
  authenticateJWT,
  authenticateDID,
  requireRole,
  registerCredentialRateLimit,
  setupGracefulShutdown,
  createConnectionDrainer,
} from '@the-order/shared';
import { IssueVCSchema, VerifyVCSchema } from '@the-order/schemas';
import { KMSClient } from '@the-order/crypto';
import {
  createVerifiableCredential,
  getVerifiableCredentialById,
  createSignature,
} from '@the-order/database';
import { getPool } from '@the-order/database';
import { randomUUID } from 'crypto';

const logger = createLogger('identity-service');

const server = Fastify({
  logger: true, // Use default logger to avoid type issues
  requestIdLogLabel: 'requestId',
  disableRequestLogging: false,
});

// Initialize database pool
const env = getEnv();
if (env.DATABASE_URL) {
  getPool({ connectionString: env.DATABASE_URL });
}

// Initialize KMS client
const kmsClient = new KMSClient({
  provider: env.KMS_TYPE || 'aws',
  keyId: env.KMS_KEY_ID,
  region: env.KMS_REGION,
});

// Initialize server
async function initializeServer(): Promise<void> {
  // Register Swagger
  const swaggerUrl = env.SWAGGER_SERVER_URL || (env.NODE_ENV === 'development' ? 'http://localhost:4002' : undefined);
  if (!swaggerUrl) {
    logger.warn('SWAGGER_SERVER_URL not set, Swagger documentation will not be available');
  } else {
    await server.register(fastifySwagger, {
      openapi: {
        info: {
          title: 'Identity Service API',
          description: 'eIDAS/DID, verifiable credentials, and identity management',
          version: '1.0.0',
        },
        servers: [
          {
            url: swaggerUrl,
            description: env.NODE_ENV || 'Development server',
          },
        ],
      },
    });

    await server.register(fastifySwaggerUI, {
      routePrefix: '/docs',
    });
  }

  // Register security plugins
  await registerSecurityPlugins(server);

  // Register credential-specific rate limiting
  await registerCredentialRateLimit(server);

  // Add middleware
  addCorrelationId(server);
  addRequestLogging(server);

  // Set error handler
  server.setErrorHandler(errorHandler);

  // Register Microsoft Entra VerifiedID routes
  const { registerEntraRoutes } = await import('./entra-integration');
  await registerEntraRoutes(server);

  // Register Entra webhook routes
  const { registerEntraWebhookRoutes } = await import('./entra-webhooks');
  await registerEntraWebhookRoutes(server);

  // Register batch issuance endpoint
  const { registerBatchIssuance } = await import('./batch-issuance');
  await registerBatchIssuance(server, kmsClient);

  // Initialize event-driven credential issuance
  if (env.REDIS_URL) {
    try {
      const { initializeEventDrivenIssuance } = await import('./event-driven-issuance');
      await initializeEventDrivenIssuance({
        kmsClient,
        autoIssueOnRegistration: true,
        autoIssueOnEIDASVerification: true,
        autoIssueOnAppointment: true,
        autoIssueOnDocumentApproval: true,
        autoIssueOnPaymentCompletion: true,
      });
      logger.info('Event-driven credential issuance initialized');
    } catch (error) {
      logger.warn('Failed to initialize event-driven issuance:', error);
    }

    // Initialize credential notifications
    try {
      const { initializeCredentialNotifications } = await import('./credential-notifications');
      await initializeCredentialNotifications();
      logger.info('Credential notifications initialized');
    } catch (error) {
      logger.warn('Failed to initialize credential notifications:', error);
    }
  }

  // Register credential template endpoints
  const { registerTemplateRoutes } = await import('./templates');
  await registerTemplateRoutes(server);

  // Register metrics endpoints
  const { registerMetricsRoutes } = await import('./metrics-routes');
  await registerMetricsRoutes(server);

  // Register judicial credential endpoints
  const { registerJudicialRoutes } = await import('./judicial-routes');
  await registerJudicialRoutes(server, kmsClient);

  // Initialize scheduled credential issuance
  if (env.REDIS_URL) {
    try {
      const { initializeScheduledIssuance } = await import('./scheduled-issuance');
      await initializeScheduledIssuance({
        kmsClient,
        enableExpirationDetection: true,
        enableBatchRenewal: true,
        enableScheduledIssuance: true,
      });
      logger.info('Scheduled credential issuance initialized');
    } catch (error) {
      logger.warn('Failed to initialize scheduled issuance:', error);
    }

    // Initialize automated judicial appointment issuance
    try {
      const { initializeJudicialAppointmentIssuance } = await import('./judicial-appointment');
      await initializeJudicialAppointmentIssuance(kmsClient);
      logger.info('Automated judicial appointment issuance initialized');
    } catch (error) {
      logger.warn('Failed to initialize judicial appointment issuance:', error);
    }

    // Initialize automated verification
    try {
      const { initializeAutomatedVerification } = await import('./automated-verification');
      await initializeAutomatedVerification(kmsClient);
      logger.info('Automated credential verification initialized');
    } catch (error) {
      logger.warn('Failed to initialize automated verification:', error);
    }
  }

  // Register Letters of Credence endpoints
  const { registerLettersOfCredenceRoutes } = await import('./letters-of-credence-routes');
  await registerLettersOfCredenceRoutes(server, kmsClient);

  // Register Financial credential endpoints
  const { registerFinancialCredentialRoutes } = await import('./financial-routes');
  await registerFinancialCredentialRoutes(server, kmsClient);
}

// Health check
server.get(
  '/health',
  {
    schema: {
      description: 'Health check endpoint',
      tags: ['health'],
      response: {
        200: {
          type: 'object',
          properties: {
            status: { type: 'string' },
            service: { type: 'string' },
            database: { type: 'string' },
            kms: { type: 'string' },
          },
        },
      },
    },
  },
  async () => {
    const { healthCheck: dbHealthCheck } = await import('@the-order/database');
    const dbHealthy = await dbHealthCheck().catch(() => false);
    
    // Check KMS availability
    let kmsHealthy = false;
    try {
      // Try a simple operation (would fail gracefully if KMS unavailable)
      const testData = Buffer.from('health-check');
      await kmsClient.sign(testData);
      kmsHealthy = true;
    } catch {
      kmsHealthy = false;
    }

    return {
      status: dbHealthy && kmsHealthy ? 'ok' : 'degraded',
      service: 'identity',
      database: dbHealthy ? 'connected' : 'disconnected',
      kms: kmsHealthy ? 'available' : 'unavailable',
    };
  }
);

// Issue verifiable credential
server.post(
  '/vc/issue',
  {
    preHandler: [authenticateJWT, requireRole('admin', 'issuer')],
    schema: {
      ...createBodySchema(IssueVCSchema),
      description: 'Issue a verifiable credential',
      tags: ['credentials'],
      response: {
        200: {
          type: 'object',
          properties: {
            credential: {
              type: 'object',
            },
          },
        },
      },
    },
  },
  async (request, _reply) => {
    const body = request.body as { subject: string; credentialSubject: Record<string, unknown>; expirationDate?: string };
    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');
    }
    
    const credentialId = randomUUID();
    const issuanceDate = new Date();
    const expirationDate = body.expirationDate ? new Date(body.expirationDate) : undefined;

    // Create credential data
    const credentialData = {
      id: credentialId,
      type: ['VerifiableCredential', 'IdentityCredential'],
      issuer: issuerDid,
      subject: body.subject,
      credentialSubject: body.credentialSubject,
      issuanceDate: issuanceDate.toISOString(),
      expirationDate: expirationDate?.toISOString(),
    };

    // Sign credential with KMS
    const credentialJson = JSON.stringify(credentialData);
    const signature = await kmsClient.sign(Buffer.from(credentialJson));

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

    const credential = {
      ...credentialData,
      proof,
    };

    // Save to database
    await createVerifiableCredential({
      credential_id: credentialId,
      issuer_did: issuerDid,
      subject_did: body.subject,
      credential_type: credential.type,
      credential_subject: body.credentialSubject,
      issuance_date: issuanceDate,
      expiration_date: expirationDate,
      proof,
    });

    return { credential };
  }
);

// Verify verifiable credential
server.post(
  '/vc/verify',
  {
    preHandler: [authenticateJWT],
    schema: {
      ...createBodySchema(VerifyVCSchema),
      description: 'Verify a verifiable credential',
      tags: ['credentials'],
      response: {
        200: {
          type: 'object',
          properties: {
            valid: { type: 'boolean' },
          },
        },
      },
    },
  },
  async (request, _reply) => {
    const body = request.body as { credential: { id: string; proof?: { jws: string; verificationMethod: string } } };
    
    // Get credential from database
    const storedVC = await getVerifiableCredentialById(body.credential.id);
    if (!storedVC) {
      return { valid: false, reason: 'Credential not found' };
    }

    // Check if revoked
    if (storedVC.revoked) {
      return { valid: false, reason: 'Credential has been revoked' };
    }

    // Check expiration
    if (storedVC.expiration_date && new Date(storedVC.expiration_date) < new Date()) {
      return { valid: false, reason: 'Credential has expired' };
    }

    // Verify signature if proof exists
    let signatureValid = true;
    if (body.credential.proof?.jws) {
      try {
        const credentialJson = JSON.stringify({
          ...body.credential,
          proof: undefined,
        });
        const signature = Buffer.from(body.credential.proof.jws, 'base64');
        
        // Verify with KMS
        signatureValid = await kmsClient.verify(Buffer.from(credentialJson), signature);
      } catch {
        signatureValid = false;
      }
    }

    const valid = signatureValid && !storedVC.revoked;

    return { valid, reason: valid ? undefined : 'Signature verification failed' };
  }
);

// Sign document
server.post(
  '/sign',
  {
    preHandler: [authenticateDID],
    schema: {
      description: 'Sign a document',
      tags: ['signing'],
      body: {
        type: 'object',
        required: ['document', 'did'],
        properties: {
          document: { type: 'string' },
          did: { type: 'string' },
        },
      },
      response: {
        200: {
          type: 'object',
          properties: {
            signature: { type: 'string' },
            timestamp: { type: 'string' },
          },
        },
      },
    },
  },
  async (request, _reply) => {
    const body = request.body as { document: string; did: string; documentId?: string };
    
    const documentBuffer = Buffer.from(body.document);
    const signature = await kmsClient.sign(documentBuffer);
    const timestamp = new Date();

    // Save signature to database if documentId provided
    if (body.documentId) {
      await createSignature({
        document_id: body.documentId,
        signer_did: body.did,
        signature_data: signature.toString('base64'),
        signature_timestamp: timestamp,
        signature_type: 'kms',
      });
    }

    return {
      signature: signature.toString('base64'),
      timestamp: timestamp.toISOString(),
    };
  }
);

// Start server
const start = async () => {
  try {
    await initializeServer();
    const env = getEnv();
    const port = env.PORT || 4002;
    await server.listen({ port, host: '0.0.0.0' });
    logger.info({ port }, 'Identity service listening');

    // Setup graceful shutdown
    const pool = getPool({ connectionString: env.DATABASE_URL });
    setupGracefulShutdown(server, {
      timeout: 30000,
      onShutdown: createConnectionDrainer(pool),
    });
  } catch (err) {
    logger.error({ err }, 'Failed to start server');
    process.exit(1);
  }
};

start();

