import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js';
import { requireOpsAuth } from '../helpers/auth.js';

export class RemoteIngressHandler {
  constructor(private opsServerRef: OpsServer) {
    this.registerHandlers();
  }

  private registerHandlers(): void {
    const viewRouter = this.opsServerRef.viewRouter;
    const adminRouter = this.opsServerRef.adminRouter;

    // ---- Read endpoints (viewRouter — valid identity required via middleware) ----

    // Get all remote ingress edges
    viewRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngresses>(
        'getRemoteIngresses',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'remote-ingress:read' });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          if (!manager) {
            return { edges: [] };
          }
          // Return edges without secrets, enriched with effective listen ports and breakdown
          const edges = manager.getAllEdges().map((e) => {
            const breakdown = manager.getPortBreakdown(e);
            return {
              ...e,
              secret: '********', // Never expose secrets via API
              effectiveListenPorts: manager.getEffectiveListenPorts(e),
              effectiveListenPortsUdp: manager.getEffectiveListenPortsUdp(e),
              manualPorts: breakdown.manual,
              derivedPorts: breakdown.derived,
            };
          });
          return { edges };
        },
      ),
    );

    // ---- Write endpoints (adminRouter) ----

    // Create a new remote ingress edge
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateRemoteIngress>(
        'createRemoteIngress',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'remote-ingress:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;

          if (!manager) {
            return {
              success: false,
              edge: null as any,
            };
          }

          const edge = await manager.createEdge(
            dataArg.name,
            dataArg.listenPorts || [],
            dataArg.tags,
            dataArg.autoDerivePorts ?? true,
            dataArg.performance,
          );

          // Sync allowed edges with the hub
          if (tunnelManager) {
            await tunnelManager.syncAllowedEdges();
          }

          return { success: true, edge };
        },
      ),
    );

    // Delete a remote ingress edge
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteRemoteIngress>(
        'deleteRemoteIngress',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'remote-ingress:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;

          if (!manager) {
            return { success: false, message: 'RemoteIngress not configured' };
          }

          const deleted = await manager.deleteEdge(dataArg.id);
          if (deleted && tunnelManager) {
            await tunnelManager.syncAllowedEdges();
          }

          return {
            success: deleted,
            message: deleted ? undefined : 'Edge not found',
          };
        },
      ),
    );

    // Update a remote ingress edge
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateRemoteIngress>(
        'updateRemoteIngress',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'remote-ingress:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;

          if (!manager) {
            return { success: false, edge: null as any };
          }

          const edge = await manager.updateEdge(dataArg.id, {
            name: dataArg.name,
            listenPorts: dataArg.listenPorts,
            autoDerivePorts: dataArg.autoDerivePorts,
            enabled: dataArg.enabled,
            performance: dataArg.performance,
            tags: dataArg.tags,
          });

          if (!edge) {
            return { success: false, edge: null as any };
          }

          // Sync allowed edges — ports, tags, or enabled may have changed
          if (tunnelManager) {
            await tunnelManager.syncAllowedEdges();
          }

          const breakdown = manager.getPortBreakdown(edge);
          return {
            success: true,
            edge: {
              ...edge,
              secret: '********',
              effectiveListenPorts: manager.getEffectiveListenPorts(edge),
              effectiveListenPortsUdp: manager.getEffectiveListenPortsUdp(edge),
              manualPorts: breakdown.manual,
              derivedPorts: breakdown.derived,
            },
          };
        },
      ),
    );

    // Regenerate secret for an edge
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RegenerateRemoteIngressSecret>(
        'regenerateRemoteIngressSecret',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'remote-ingress:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;

          if (!manager) {
            return { success: false, secret: '' };
          }

          const secret = await manager.regenerateSecret(dataArg.id);
          if (!secret) {
            return { success: false, secret: '' };
          }

          // Sync allowed edges since secret changed
          if (tunnelManager) {
            await tunnelManager.syncAllowedEdges();
          }

          return { success: true, secret };
        },
      ),
    );

    // Get runtime status of all edges (read)
    viewRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressStatus>(
        'getRemoteIngressStatus',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'remote-ingress:read' });
          const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
          if (!tunnelManager) {
            return { statuses: [] };
          }
          return { statuses: tunnelManager.getEdgeStatuses() };
        },
      ),
    );

    // Get a connection token for an edge (write — exposes secret)
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressConnectionToken>(
        'getRemoteIngressConnectionToken',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'remote-ingress:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          if (!manager) {
            return { success: false, message: 'RemoteIngress not configured' };
          }

          const edge = manager.getEdge(dataArg.edgeId);
          if (!edge) {
            return { success: false, message: 'Edge not found' };
          }
          if (!edge.enabled) {
            return { success: false, message: 'Edge is disabled' };
          }

          const hubHost = dataArg.hubHost
            || this.opsServerRef.dcRouterRef.options.remoteIngressConfig?.hubDomain;
          if (!hubHost) {
            return {
              success: false,
              message: 'No hub hostname configured. Set hubDomain in remoteIngressConfig or provide hubHost.',
            };
          }

          const hubPort = this.opsServerRef.dcRouterRef.options.remoteIngressConfig?.tunnelPort ?? 8443;

          const token = plugins.remoteingress.encodeConnectionToken({
            hubHost,
            hubPort,
            edgeId: edge.id,
            secret: edge.secret,
          });

          return { success: true, token };
        },
      ),
    );
  }
}
