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';

function hasOwn(objectArg: object, keyArg: string): boolean {
  return Object.prototype.hasOwnProperty.call(objectArg, keyArg);
}

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,
          });
          try {
            const edge = await this.opsServerRef.dcRouterRef.mutateRemoteIngressEdges((manager) => manager.createEdge(
              dataArg.name,
              dataArg.listenPorts || [],
              dataArg.tags,
              dataArg.autoDerivePorts ?? true,
              dataArg.performance,
            ));
            return { success: true, edge };
          } catch (err: unknown) {
            return {
              success: false,
              edge: null as any,
            };
          }
        },
      ),
    );

    // 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 deleted = await this.opsServerRef.dcRouterRef.mutateRemoteIngressEdges(
            (manager) => manager.deleteEdge(dataArg.id),
          ).catch((err: unknown) => {
            if ((err as Error).message.includes('RemoteIngress')) {
              return false;
            }
            throw err;
          });

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

    // 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 result = await this.opsServerRef.dcRouterRef.mutateRemoteIngressEdges(async (manager) => {
            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 null;
            }

            const breakdown = manager.getPortBreakdown(edge);
            return {
              ...edge,
              secret: '********',
              effectiveListenPorts: manager.getEffectiveListenPorts(edge),
              effectiveListenPortsUdp: manager.getEffectiveListenPortsUdp(edge),
              manualPorts: breakdown.manual,
              derivedPorts: breakdown.derived,
            };
          }).catch((err: unknown) => {
            if ((err as Error).message.includes('RemoteIngress')) {
              return null;
            }
            throw err;
          });

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

    // 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 secret = await this.opsServerRef.dcRouterRef.mutateRemoteIngressEdges(
            (manager) => manager.regenerateSecret(dataArg.id),
          ).catch((err: unknown) => {
            if ((err as Error).message.includes('RemoteIngress')) {
              return null;
            }
            throw err;
          });
          if (!secret) {
            return { success: false, secret: '' };
          }

          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 hub-level settings (read)
    viewRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressHubSettings>(
        'getRemoteIngressHubSettings',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'remote-ingress:read' });
          const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
          return {
            settings: manager?.getHubSettings() || {
              enabled: false,
              tunnelPort: 8443,
              updatedAt: 0,
              updatedBy: 'default',
            },
          };
        },
      ),
    );

    // Update hub-level settings (write)
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateRemoteIngressHubSettings>(
        'updateRemoteIngressHubSettings',
        async (dataArg, toolsArg) => {
          const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'remote-ingress:write',
            requireAdminIdentity: true,
          });

          try {
            const updates: interfaces.data.TRemoteIngressHubSettingsUpdate = {};
            if (hasOwn(dataArg, 'enabled') && dataArg.enabled !== undefined) {
              updates.enabled = dataArg.enabled;
            }
            if (hasOwn(dataArg, 'tunnelPort') && dataArg.tunnelPort !== undefined) {
              updates.tunnelPort = dataArg.tunnelPort;
            }
            if (hasOwn(dataArg, 'hubDomain')) {
              updates.hubDomain = dataArg.hubDomain ?? null;
            }
            if (hasOwn(dataArg, 'performance')) {
              updates.performance = dataArg.performance ?? null;
            }

            const settings = await this.opsServerRef.dcRouterRef.updateRemoteIngressHubSettings(
              updates,
              auth.userId,
            );
            return { success: true, settings };
          } catch (err: unknown) {
            return { success: false, message: (err as Error).message };
          }
        },
      ),
    );

    // 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 hubSettings = manager.getHubSettings();
          const hubHost = dataArg.hubHost || hubSettings.hubDomain;
          if (!hubHost) {
            return {
              success: false,
              message: 'No hub hostname configured. Set the RemoteIngress hub domain or provide hubHost.',
            };
          }

          const hubPort = hubSettings.tunnelPort;

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

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