/**
 * Cloudflare Domain Sync Manager
 *
 * Synchronizes Cloudflare DNS zones with the local Domain table.
 * Automatically imports zones and marks obsolete domains when zones are removed.
 */

import * as plugins from '../plugins.ts';
import { logger } from '../logging.ts';
import { getErrorMessage } from '../utils/error.ts';
import { OneboxDatabase } from './database.ts';
import type { IDomain } from '../types.ts';

export class CloudflareDomainSync {
  private database: OneboxDatabase;
  private cloudflareAccount: plugins.cloudflare.CloudflareAccount | null = null;

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

  /**
   * Initialize Cloudflare connection
   */
  async init(): Promise<void> {
    try {
      const apiKey = await this.database.getSecretSetting('cloudflareToken');

      if (!apiKey) {
        logger.warn('Cloudflare API key not configured. Domain sync will be limited.');
        return;
      }

      this.cloudflareAccount = new plugins.cloudflare.CloudflareAccount(apiKey);
      logger.info('Cloudflare domain sync initialized');
    } catch (error) {
      logger.error(`Failed to initialize Cloudflare sync: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Check if Cloudflare is configured
   */
  isConfigured(): boolean {
    return this.cloudflareAccount !== null;
  }

  /**
   * Sync all Cloudflare zones with Domain table
   */
  async syncZones(): Promise<void> {
    if (!this.isConfigured()) {
      logger.warn('Cloudflare not configured, skipping zone sync');
      return;
    }

    try {
      logger.info('Starting Cloudflare zone synchronization...');

      // Fetch all zones from Cloudflare (v6+ API uses convenience.listZones())
      const zones = await this.cloudflareAccount!.convenience.listZones();
      logger.info(`Found ${zones.length} Cloudflare zone(s)`);

      const now = Date.now();
      const syncedZoneIds = new Set<string>();

      // Sync each zone to the Domain table
      for (const zone of zones) {
        try {
          const domain = zone.name;
          const zoneId = zone.id;

          syncedZoneIds.add(zoneId);

          // Check if domain already exists
          const existingDomain = this.database.getDomainByName(domain);

          if (existingDomain) {
            // Update existing domain
            this.database.updateDomain(existingDomain.id!, {
              dnsProvider: 'cloudflare',
              cloudflareZoneId: zoneId,
              isObsolete: false, // Re-activate if it was marked obsolete
              updatedAt: now,
            });
            logger.debug(`Updated domain: ${domain}`);
          } else {
            // Create new domain
            this.database.createDomain({
              domain,
              dnsProvider: 'cloudflare',
              cloudflareZoneId: zoneId,
              isObsolete: false,
              defaultWildcard: true, // Default to wildcard certificates
              createdAt: now,
              updatedAt: now,
            });
            logger.info(`Added new domain from Cloudflare: ${domain}`);
          }
        } catch (error) {
          logger.error(`Failed to sync zone ${zone.name}: ${getErrorMessage(error)}`);
        }
      }

      // Mark domains as obsolete if their Cloudflare zones no longer exist
      await this.markObsoleteDomains(syncedZoneIds);

      logger.success(`Cloudflare zone sync completed: ${zones.length} zone(s) synced`);
    } catch (error) {
      logger.error(`Cloudflare zone sync failed: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Mark domains as obsolete if their Cloudflare zones have been removed
   */
  private async markObsoleteDomains(activeZoneIds: Set<string>): Promise<void> {
    try {
      // Get all domains managed by Cloudflare
      const cloudflareDomains = this.database.getDomainsByProvider('cloudflare');

      let obsoleteCount = 0;

      for (const domain of cloudflareDomains) {
        // If domain has a Cloudflare zone ID but it's not in the active set, mark obsolete
        if (domain.cloudflareZoneId && !activeZoneIds.has(domain.cloudflareZoneId)) {
          this.database.updateDomain(domain.id!, {
            isObsolete: true,
            updatedAt: Date.now(),
          });
          logger.warn(`Marked domain as obsolete (zone removed): ${domain.domain}`);
          obsoleteCount++;
        }
      }

      if (obsoleteCount > 0) {
        logger.info(`Marked ${obsoleteCount} domain(s) as obsolete`);
      }
    } catch (error) {
      logger.error(`Failed to mark obsolete domains: ${getErrorMessage(error)}`);
    }
  }

  /**
   * Get sync status information
   */
  getSyncStatus(): {
    configured: boolean;
    totalDomains: number;
    cloudflareDomains: number;
    obsoleteDomains: number;
  } {
    const allDomains = this.database.getAllDomains();
    const cloudflareDomains = allDomains.filter(d => d.dnsProvider === 'cloudflare');
    const obsoleteDomains = allDomains.filter(d => d.isObsolete);

    return {
      configured: this.isConfigured(),
      totalDomains: allDomains.length,
      cloudflareDomains: cloudflareDomains.length,
      obsoleteDomains: obsoleteDomains.length,
    };
  }
}
