/**
 * Database-backed Certificate Manager for SmartACME
 *
 * Implements ICertManager interface to store SSL certificates directly in SmartDB.
 * Certificate PEM content is stored in the database, not on the filesystem.
 */

import * as plugins from '../plugins.js';
import { logger } from '../logging.js';
import { getErrorMessage } from '../utils/error.js';
import { OneboxDatabase } from '../database/index.js';

export class DatabaseCertManager implements plugins.smartacme.ICertManager {
  private database: OneboxDatabase;

  constructor(database: OneboxDatabase) {
    this.database = database;
  }

  /**
   * Initialize the certificate manager
   */
  async init(): Promise<void> {
    logger.info('Certificate manager initialized (database storage)');
  }

  /**
   * Retrieve a certificate by domain name
   */
  async retrieveCertificate(domainName: string): Promise<plugins.smartacme.Cert | null> {
    try {
      const dbCert = await this.database.getSSLCertificate(domainName);

      if (!dbCert || !dbCert.keyPem || !dbCert.fullchainPem) {
        return null;
      }

      // Convert database format to SmartacmeCert format
      const cert = new plugins.smartacme.Cert({
        id: dbCert.id?.toString() || domainName,
        domainName: dbCert.domain,
        created: dbCert.createdAt,
        privateKey: dbCert.keyPem,
        publicKey: dbCert.fullchainPem, // Full chain as public key
        csr: '', // CSR not stored separately
        validUntil: dbCert.expiryDate,
      });

      return cert;
    } catch (error) {
      logger.warn(`Failed to retrieve certificate for ${domainName}: ${getErrorMessage(error)}`);
      return null;
    }
  }

  /**
   * Store a certificate
   */
  async storeCertificate(cert: plugins.smartacme.Cert): Promise<void> {
    try {
      const domain = cert.domainName;

      // Extract certificate from full chain (first certificate in the chain)
      const certPem = this.extractCertFromChain(cert.publicKey);

      // Store/update in database with PEM content
      const existing = await this.database.getSSLCertificate(domain);

      if (existing) {
        await this.database.updateSSLCertificate(domain, {
          certPem: certPem,
          keyPem: cert.privateKey,
          fullchainPem: cert.publicKey,
          expiryDate: cert.validUntil,
        });
      } else {
        await this.database.createSSLCertificate({
          domain,
          certPem: certPem,
          keyPem: cert.privateKey,
          fullchainPem: cert.publicKey,
          expiryDate: cert.validUntil,
          issuer: "Let's Encrypt",
          createdAt: cert.created,
          updatedAt: Date.now(),
        });
      }

      logger.success(`Certificate stored for ${domain}`);
    } catch (error) {
      logger.error(`Failed to store certificate for ${cert.domainName}: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Delete a certificate
   */
  async deleteCertificate(domainName: string): Promise<void> {
    try {
      const dbCert = await this.database.getSSLCertificate(domainName);

      if (dbCert) {
        // Delete from database
        await this.database.deleteSSLCertificate(domainName);
        logger.info(`Certificate deleted for ${domainName}`);
      }
    } catch (error) {
      logger.error(`Failed to delete certificate for ${domainName}: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Close the certificate manager
   */
  async close(): Promise<void> {
    // SmartDB is managed by OneboxDatabase, nothing to close here
    logger.info('Certificate manager closed');
  }

  /**
   * Wipe all certificates (for testing)
   */
  async wipe(): Promise<void> {
    try {
      const certs = await this.database.getAllSSLCertificates();

      for (const cert of certs) {
        await this.deleteCertificate(cert.domain);
      }

      logger.warn('All certificates wiped');
    } catch (error) {
      logger.error(`Failed to wipe certificates: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Extract the first certificate from a PEM chain
   */
  private extractCertFromChain(fullChain: string): string {
    const certMatch = fullChain.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/);
    return certMatch ? certMatch[0] : fullChain;
  }
}
