import * as plugins from '../../plugins.ts';
import type { OpsServer } from '../classes.opsserver.ts';
import * as interfaces from '../../../ts_interfaces/index.ts';
import { requireAdminIdentity } from '../helpers/guards.ts';

export class DomainsHandler {
  public typedrouter = new plugins.typedrequest.TypedRouter();

  constructor(private opsServerRef: OpsServer) {
    this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
    this.registerHandlers();
  }

  private buildDomainViews(): interfaces.data.IDomainDetail[] {
    const domains = this.opsServerRef.oneboxRef.database.getAllDomains();
    const allServices = this.opsServerRef.oneboxRef.database.getAllServices();

    return domains.map((domain: any) => {
      const certificates = this.opsServerRef.oneboxRef.database.getCertificatesByDomain(domain.id!);
      const requirements = this.opsServerRef.oneboxRef.database.getCertRequirementsByDomain(domain.id!);

      const serviceCount = allServices.filter((service: any) => {
        if (!service.domain) return false;
        const baseDomain = service.domain.split('.').slice(-2).join('.');
        return baseDomain === domain.domain;
      }).length;

      let certificateStatus: 'valid' | 'expiring-soon' | 'expired' | 'pending' | 'none' = 'none';
      let daysRemaining: number | null = null;

      const validCerts = certificates.filter((cert: any) => cert.isValid && cert.expiryDate > Date.now());
      if (validCerts.length > 0) {
        const latestCert = validCerts.reduce((latest: any, cert: any) =>
          cert.expiryDate > latest.expiryDate ? cert : latest
        );
        daysRemaining = Math.floor((latestCert.expiryDate - Date.now()) / (24 * 60 * 60 * 1000));
        certificateStatus = daysRemaining <= 30 ? 'expiring-soon' : 'valid';
      } else if (certificates.some((cert: any) => !cert.isValid)) {
        certificateStatus = 'expired';
      } else if (requirements.some((req: any) => req.status === 'pending')) {
        certificateStatus = 'pending';
      }

      return {
        domain,
        certificates,
        requirements,
        serviceCount,
        certificateStatus,
        daysRemaining,
      };
    });
  }

  private registerHandlers(): void {
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDomains>(
        'getDomains',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const domains = this.buildDomainViews();
          return { domains };
        },
      ),
    );

    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDomain>(
        'getDomain',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const domain = this.opsServerRef.oneboxRef.database.getDomainByName(dataArg.domainName);
          if (!domain) {
            throw new plugins.typedrequest.TypedResponseError('Domain not found');
          }
          const views = this.buildDomainViews();
          const domainView = views.find((v) => v.domain.domain === dataArg.domainName);
          if (!domainView) {
            throw new plugins.typedrequest.TypedResponseError('Domain not found');
          }
          return { domain: domainView };
        },
      ),
    );

    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SyncDomains>(
        'syncDomains',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          if (!this.opsServerRef.oneboxRef.cloudflareDomainSync) {
            throw new plugins.typedrequest.TypedResponseError('Cloudflare domain sync not configured');
          }
          await this.opsServerRef.oneboxRef.cloudflareDomainSync.syncZones();
          const domains = this.buildDomainViews();
          return { domains };
        },
      ),
    );

    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetGatewayDomains>(
        'getGatewayDomains',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const domains = await this.opsServerRef.oneboxRef.externalGateway.getGatewayDomains();
          return { domains };
        },
      ),
    );
  }
}
