/**
 * Certificate Repository
 * Handles CRUD operations for domains, certificates, cert_requirements, and legacy ssl_certificates
 */

import { BaseRepository } from '../base.repository.ts';
import type { TBindValue } from '../types.ts';
import type { IDomain, ICertificate, ICertRequirement, ISslCertificate } from '../../types.ts';

export class CertificateRepository extends BaseRepository {
  // ============ Domains ============

  createDomain(domain: Omit<IDomain, 'id'>): IDomain {
    this.query(
      `INSERT INTO domains (domain, dns_provider, cloudflare_zone_id, is_obsolete, default_wildcard, created_at, updated_at)
       VALUES (?, ?, ?, ?, ?, ?, ?)`,
      [
        domain.domain,
        domain.dnsProvider,
        domain.cloudflareZoneId,
        domain.isObsolete ? 1 : 0,
        domain.defaultWildcard ? 1 : 0,
        domain.createdAt,
        domain.updatedAt,
      ]
    );

    return this.getDomainByName(domain.domain)!;
  }

  getDomainByName(domain: string): IDomain | null {
    const rows = this.query('SELECT * FROM domains WHERE domain = ?', [domain]);
    return rows.length > 0 ? this.rowToDomain(rows[0]) : null;
  }

  getDomainById(id: number): IDomain | null {
    const rows = this.query('SELECT * FROM domains WHERE id = ?', [id]);
    return rows.length > 0 ? this.rowToDomain(rows[0]) : null;
  }

  getAllDomains(): IDomain[] {
    const rows = this.query('SELECT * FROM domains ORDER BY domain ASC');
    return rows.map((row) => this.rowToDomain(row));
  }

  getDomainsByProvider(provider: NonNullable<IDomain['dnsProvider']>): IDomain[] {
    const rows = this.query('SELECT * FROM domains WHERE dns_provider = ? ORDER BY domain ASC', [provider]);
    return rows.map((row) => this.rowToDomain(row));
  }

  updateDomain(id: number, updates: Partial<IDomain>): void {
    const fields: string[] = [];
    const values: TBindValue[] = [];

    if (updates.domain !== undefined) {
      fields.push('domain = ?');
      values.push(updates.domain);
    }
    if (updates.dnsProvider !== undefined) {
      fields.push('dns_provider = ?');
      values.push(updates.dnsProvider);
    }
    if (updates.cloudflareZoneId !== undefined) {
      fields.push('cloudflare_zone_id = ?');
      values.push(updates.cloudflareZoneId);
    }
    if (updates.isObsolete !== undefined) {
      fields.push('is_obsolete = ?');
      values.push(updates.isObsolete ? 1 : 0);
    }
    if (updates.defaultWildcard !== undefined) {
      fields.push('default_wildcard = ?');
      values.push(updates.defaultWildcard ? 1 : 0);
    }

    fields.push('updated_at = ?');
    values.push(Date.now());
    values.push(id);

    this.query(`UPDATE domains SET ${fields.join(', ')} WHERE id = ?`, values);
  }

  deleteDomain(id: number): void {
    this.query('DELETE FROM domains WHERE id = ?', [id]);
  }

  private rowToDomain(row: any): IDomain {
    return {
      id: Number(row.id || row[0]),
      domain: String(row.domain || row[1]),
      dnsProvider: (row.dns_provider || row[2]) as IDomain['dnsProvider'],
      cloudflareZoneId: row.cloudflare_zone_id || row[3] || undefined,
      isObsolete: Boolean(row.is_obsolete || row[4]),
      defaultWildcard: Boolean(row.default_wildcard || row[5]),
      createdAt: Number(row.created_at || row[6]),
      updatedAt: Number(row.updated_at || row[7]),
    };
  }

  // ============ Certificates ============

  createCertificate(cert: Omit<ICertificate, 'id'>): ICertificate {
    this.query(
      `INSERT INTO certificates (domain_id, cert_domain, is_wildcard, cert_pem, key_pem, fullchain_pem, expiry_date, issuer, is_valid, created_at, updated_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        cert.domainId,
        cert.certDomain,
        cert.isWildcard ? 1 : 0,
        cert.certPem,
        cert.keyPem,
        cert.fullchainPem,
        cert.expiryDate,
        cert.issuer,
        cert.isValid ? 1 : 0,
        cert.createdAt,
        cert.updatedAt,
      ]
    );

    const rows = this.query('SELECT * FROM certificates WHERE id = last_insert_rowid()');
    return this.rowToCertificate(rows[0]);
  }

  getCertificateById(id: number): ICertificate | null {
    const rows = this.query('SELECT * FROM certificates WHERE id = ?', [id]);
    return rows.length > 0 ? this.rowToCertificate(rows[0]) : null;
  }

  getCertificatesByDomain(domainId: number): ICertificate[] {
    const rows = this.query('SELECT * FROM certificates WHERE domain_id = ? ORDER BY expiry_date DESC', [domainId]);
    return rows.map((row) => this.rowToCertificate(row));
  }

  getAllCertificates(): ICertificate[] {
    const rows = this.query('SELECT * FROM certificates ORDER BY expiry_date DESC');
    return rows.map((row) => this.rowToCertificate(row));
  }

  updateCertificate(id: number, updates: Partial<ICertificate>): void {
    const fields: string[] = [];
    const values: TBindValue[] = [];

    if (updates.certDomain !== undefined) {
      fields.push('cert_domain = ?');
      values.push(updates.certDomain);
    }
    if (updates.isWildcard !== undefined) {
      fields.push('is_wildcard = ?');
      values.push(updates.isWildcard ? 1 : 0);
    }
    if (updates.certPem !== undefined) {
      fields.push('cert_pem = ?');
      values.push(updates.certPem);
    }
    if (updates.keyPem !== undefined) {
      fields.push('key_pem = ?');
      values.push(updates.keyPem);
    }
    if (updates.fullchainPem !== undefined) {
      fields.push('fullchain_pem = ?');
      values.push(updates.fullchainPem);
    }
    if (updates.expiryDate !== undefined) {
      fields.push('expiry_date = ?');
      values.push(updates.expiryDate);
    }
    if (updates.issuer !== undefined) {
      fields.push('issuer = ?');
      values.push(updates.issuer);
    }
    if (updates.isValid !== undefined) {
      fields.push('is_valid = ?');
      values.push(updates.isValid ? 1 : 0);
    }

    fields.push('updated_at = ?');
    values.push(Date.now());
    values.push(id);

    this.query(`UPDATE certificates SET ${fields.join(', ')} WHERE id = ?`, values);
  }

  deleteCertificate(id: number): void {
    this.query('DELETE FROM certificates WHERE id = ?', [id]);
  }

  private rowToCertificate(row: any): ICertificate {
    return {
      id: Number(row.id || row[0]),
      domainId: Number(row.domain_id || row[1]),
      certDomain: String(row.cert_domain || row[2]),
      isWildcard: Boolean(row.is_wildcard || row[3]),
      certPem: String(row.cert_pem || row[4] || ''),
      keyPem: String(row.key_pem || row[5] || ''),
      fullchainPem: String(row.fullchain_pem || row[6] || ''),
      expiryDate: Number(row.expiry_date || row[7]),
      issuer: String(row.issuer || row[8]),
      isValid: Boolean(row.is_valid || row[9]),
      createdAt: Number(row.created_at || row[10]),
      updatedAt: Number(row.updated_at || row[11]),
    };
  }

  // ============ Certificate Requirements ============

  createCertRequirement(req: Omit<ICertRequirement, 'id'>): ICertRequirement {
    this.query(
      `INSERT INTO cert_requirements (service_id, domain_id, subdomain, certificate_id, status, created_at, updated_at)
       VALUES (?, ?, ?, ?, ?, ?, ?)`,
      [
        req.serviceId,
        req.domainId,
        req.subdomain,
        req.certificateId,
        req.status,
        req.createdAt,
        req.updatedAt,
      ]
    );

    const rows = this.query('SELECT * FROM cert_requirements WHERE id = last_insert_rowid()');
    return this.rowToCertRequirement(rows[0]);
  }

  getCertRequirementById(id: number): ICertRequirement | null {
    const rows = this.query('SELECT * FROM cert_requirements WHERE id = ?', [id]);
    return rows.length > 0 ? this.rowToCertRequirement(rows[0]) : null;
  }

  getCertRequirementsByService(serviceId: number): ICertRequirement[] {
    const rows = this.query('SELECT * FROM cert_requirements WHERE service_id = ?', [serviceId]);
    return rows.map((row) => this.rowToCertRequirement(row));
  }

  getCertRequirementsByDomain(domainId: number): ICertRequirement[] {
    const rows = this.query('SELECT * FROM cert_requirements WHERE domain_id = ?', [domainId]);
    return rows.map((row) => this.rowToCertRequirement(row));
  }

  getAllCertRequirements(): ICertRequirement[] {
    const rows = this.query('SELECT * FROM cert_requirements ORDER BY created_at DESC');
    return rows.map((row) => this.rowToCertRequirement(row));
  }

  updateCertRequirement(id: number, updates: Partial<ICertRequirement>): void {
    const fields: string[] = [];
    const values: TBindValue[] = [];

    if (updates.subdomain !== undefined) {
      fields.push('subdomain = ?');
      values.push(updates.subdomain);
    }
    if (updates.certificateId !== undefined) {
      fields.push('certificate_id = ?');
      values.push(updates.certificateId);
    }
    if (updates.status !== undefined) {
      fields.push('status = ?');
      values.push(updates.status);
    }

    fields.push('updated_at = ?');
    values.push(Date.now());
    values.push(id);

    this.query(`UPDATE cert_requirements SET ${fields.join(', ')} WHERE id = ?`, values);
  }

  deleteCertRequirement(id: number): void {
    this.query('DELETE FROM cert_requirements WHERE id = ?', [id]);
  }

  private rowToCertRequirement(row: any): ICertRequirement {
    return {
      id: Number(row.id || row[0]),
      serviceId: Number(row.service_id || row[1]),
      domainId: Number(row.domain_id || row[2]),
      subdomain: String(row.subdomain || row[3]),
      certificateId: row.certificate_id || row[4] || undefined,
      status: String(row.status || row[5]) as ICertRequirement['status'],
      createdAt: Number(row.created_at || row[6]),
      updatedAt: Number(row.updated_at || row[7]),
    };
  }

  // ============ SSL Certificates (Legacy API) ============

  async createSSLCertificate(cert: Omit<ISslCertificate, 'id'>): Promise<ISslCertificate> {
    // First, ensure domain exists in domains table
    let domainRecord = this.getDomainByName(cert.domain);
    if (!domainRecord) {
      const now = Date.now();
      domainRecord = this.createDomain({
        domain: cert.domain,
        dnsProvider: null,
        isObsolete: false,
        defaultWildcard: true,
        createdAt: now,
        updatedAt: now,
      });
    }

    const now = Date.now();
    this.query(
      `INSERT INTO certificates (domain_id, cert_domain, is_wildcard, cert_pem, key_pem, fullchain_pem, expiry_date, issuer, is_valid, created_at, updated_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        domainRecord.id,
        cert.domain,
        0,
        cert.certPem,
        cert.keyPem,
        cert.fullchainPem,
        cert.expiryDate,
        cert.issuer,
        1,
        now,
        now,
      ]
    );

    return this.getSSLCertificate(cert.domain)!;
  }

  getSSLCertificate(domain: string): ISslCertificate | null {
    const rows = this.query('SELECT * FROM certificates WHERE cert_domain = ?', [domain]);
    return rows.length > 0 ? this.rowToSSLCert(rows[0]) : null;
  }

  getAllSSLCertificates(): ISslCertificate[] {
    const rows = this.query('SELECT * FROM certificates ORDER BY expiry_date ASC');
    return rows.map((row) => this.rowToSSLCert(row));
  }

  updateSSLCertificate(domain: string, updates: Partial<ISslCertificate>): void {
    const fields: string[] = [];
    const values: TBindValue[] = [];

    if (updates.certPem) {
      fields.push('cert_pem = ?');
      values.push(updates.certPem);
    }
    if (updates.keyPem) {
      fields.push('key_pem = ?');
      values.push(updates.keyPem);
    }
    if (updates.fullchainPem) {
      fields.push('fullchain_pem = ?');
      values.push(updates.fullchainPem);
    }
    if (updates.expiryDate) {
      fields.push('expiry_date = ?');
      values.push(updates.expiryDate);
    }

    fields.push('updated_at = ?');
    values.push(Date.now());
    values.push(domain);

    this.query(`UPDATE certificates SET ${fields.join(', ')} WHERE cert_domain = ?`, values);
  }

  deleteSSLCertificate(domain: string): void {
    this.query('DELETE FROM certificates WHERE cert_domain = ?', [domain]);
  }

  private rowToSSLCert(row: any): ISslCertificate {
    return {
      id: Number(row.id || row[0]),
      domain: String(row.cert_domain || row[2] || ''),
      certPem: String(row.cert_pem || row[4] || ''),
      keyPem: String(row.key_pem || row[5] || ''),
      fullchainPem: String(row.fullchain_pem || row[6] || ''),
      expiryDate: Number(row.expiry_date || row[7]),
      issuer: String(row.issuer || row[8]),
      createdAt: Number(row.created_at || row[10]),
      updatedAt: Number(row.updated_at || row[11]),
    };
  }
}
