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

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

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

  private registerHandlers(): void {
    // Get all services
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServices>(
        'getServices',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const services = this.opsServerRef.oneboxRef.services.listServices();
          return { services };
        },
      ),
    );

    // Get single service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetService>(
        'getService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          if (!service) {
            throw new plugins.typedrequest.TypedResponseError('Service not found');
          }
          return { service };
        },
      ),
    );

    // Create service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateService>(
        'createService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const service = await this.opsServerRef.oneboxRef.services.deployService(dataArg.serviceConfig);
          return { service };
        },
      ),
    );

    // Update service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateService>(
        'updateService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const service = await this.opsServerRef.oneboxRef.services.updateService(
            dataArg.serviceName,
            dataArg.updates,
          );
          return { service };
        },
      ),
    );

    // Delete service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteService>(
        'deleteService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          await this.opsServerRef.oneboxRef.services.removeService(dataArg.serviceName);
          return { ok: true };
        },
      ),
    );

    // Start service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_StartService>(
        'startService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          await this.opsServerRef.oneboxRef.services.startService(dataArg.serviceName);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          return { service: service! };
        },
      ),
    );

    // Stop service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_StopService>(
        'stopService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          await this.opsServerRef.oneboxRef.services.stopService(dataArg.serviceName);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          return { service: service! };
        },
      ),
    );

    // Restart service
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RestartService>(
        'restartService',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          await this.opsServerRef.oneboxRef.services.restartService(dataArg.serviceName);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          return { service: service! };
        },
      ),
    );

    // Get service logs
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServiceLogs>(
        'getServiceLogs',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const logs = await this.opsServerRef.oneboxRef.services.getServiceLogs(dataArg.serviceName);
          return { logs: String(logs) };
        },
      ),
    );

    // Get service stats
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServiceStats>(
        'getServiceStats',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          if (!service || !service.containerID) {
            throw new plugins.typedrequest.TypedResponseError('Service has no container');
          }
          const stats = await this.opsServerRef.oneboxRef.docker.getContainerStats(service.containerID);
          if (!stats) {
            throw new plugins.typedrequest.TypedResponseError('Could not retrieve container stats');
          }
          return { stats };
        },
      ),
    );

    // Get service metrics
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServiceMetrics>(
        'getServiceMetrics',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          if (!service || !service.id) {
            throw new plugins.typedrequest.TypedResponseError('Service not found');
          }
          const metrics = this.opsServerRef.oneboxRef.database.getMetrics(service.id, dataArg.limit || 60);
          return { metrics };
        },
      ),
    );

    // Get service platform resources
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServicePlatformResources>(
        'getServicePlatformResources',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const rawResources = await this.opsServerRef.oneboxRef.services.getServicePlatformResources(
            dataArg.serviceName,
          );
          const resources = rawResources.map((r: any) => ({
            id: r.resource.id,
            resourceType: r.resource.resourceType,
            resourceName: r.resource.resourceName,
            platformService: {
              type: r.platformService.type,
              name: r.platformService.name,
              status: r.platformService.status,
            },
            envVars: Object.keys(r.credentials).reduce((acc: Record<string, string>, key: string) => {
              const value = r.credentials[key];
              if (key.toLowerCase().includes('password') || key.toLowerCase().includes('secret')) {
                acc[key] = '********';
              } else {
                acc[key] = value;
              }
              return acc;
            }, {}),
            createdAt: r.resource.createdAt,
          }));
          return { resources };
        },
      ),
    );

    // Get service backups
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServiceBackups>(
        'getServiceBackups',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const backups = this.opsServerRef.oneboxRef.backupManager.listBackups(dataArg.serviceName);
          return { backups };
        },
      ),
    );

    // Create service backup
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateServiceBackup>(
        'createServiceBackup',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const result = await this.opsServerRef.oneboxRef.backupManager.createBackup(dataArg.serviceName);
          return { backup: result.backup };
        },
      ),
    );

    // Get service backup schedules
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServiceBackupSchedules>(
        'getServiceBackupSchedules',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const service = this.opsServerRef.oneboxRef.services.getService(dataArg.serviceName);
          if (!service) {
            throw new plugins.typedrequest.TypedResponseError('Service not found');
          }
          const schedules = this.opsServerRef.oneboxRef.backupScheduler.getSchedulesForService(
            dataArg.serviceName,
          );
          return { schedules };
        },
      ),
    );
  }
}
