import * as plugins from './plugins.js';
import * as interfaces from '../ts_interfaces/index.js';

// Create main app state instance
export const appState = new plugins.domtools.plugins.smartstate.Smartstate();

// Define state interfaces
export interface ILoginState {
  identity: interfaces.data.IIdentity | null;
  isLoggedIn: boolean;
}

export interface IStatsState {
  serverStats: interfaces.data.IServerStats | null;
  emailStats: interfaces.data.IEmailStats | null;
  dnsStats: interfaces.data.IDnsStats | null;
  securityMetrics: interfaces.data.ISecurityMetrics | null;
  lastUpdated: number;
  isLoading: boolean;
  error: string | null;
}

export interface IConfigState {
  config: interfaces.requests.IConfigData | null;
  isLoading: boolean;
  error: string | null;
}

export interface IUiState {
  activeView: string;
  sidebarCollapsed: boolean;
  autoRefresh: boolean;
  refreshInterval: number; // milliseconds
  theme: 'light' | 'dark';
}

export interface ILogState {
  recentLogs: interfaces.data.ILogEntry[];
  isStreaming: boolean;
  filters: {
    level?: string[];
    category?: string[];
  };
}

export interface INetworkState {
  connections: interfaces.data.IConnectionInfo[];
  connectionsByIP: { [ip: string]: number };
  throughputRate: { bytesInPerSecond: number; bytesOutPerSecond: number };
  totalBytes: { in: number; out: number };
  topIPs: Array<{ ip: string; count: number }>;
  throughputByIP: Array<{ ip: string; in: number; out: number }>;
  throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
  requestsPerSecond: number;
  requestsTotal: number;
  backends: interfaces.data.IBackendInfo[];
  lastUpdated: number;
  isLoading: boolean;
  error: string | null;
}

export interface ICertificateState {
  certificates: interfaces.requests.ICertificateInfo[];
  summary: { total: number; valid: number; expiring: number; expired: number; failed: number; unknown: number };
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export interface IEmailOpsState {
  emails: interfaces.requests.IEmail[];
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

// Create state parts with appropriate persistence
export const loginStatePart = await appState.getStatePart<ILoginState>(
  'login',
  {
    identity: null,
    isLoggedIn: false,
  },
  'persistent' // Login state persists across browser sessions
);

export const statsStatePart = await appState.getStatePart<IStatsState>(
  'stats',
  {
    serverStats: null,
    emailStats: null,
    dnsStats: null,
    securityMetrics: null,
    lastUpdated: 0,
    isLoading: false,
    error: null,
  },
  'soft' // Stats are cached but not persisted
);

export const configStatePart = await appState.getStatePart<IConfigState>(
  'config',
  {
    config: null,
    isLoading: false,
    error: null,
  }
);

// Determine initial view from URL path
const getInitialView = (): string => {
  const path = typeof window !== 'undefined' ? window.location.pathname : '/';
  const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'securityprofiles', 'networktargets'];
  const segments = path.split('/').filter(Boolean);
  const view = segments[0];
  return validViews.includes(view) ? view : 'overview';
};

export const uiStatePart = await appState.getStatePart<IUiState>(
  'ui',
  {
    activeView: getInitialView(),
    sidebarCollapsed: false,
    autoRefresh: true,
    refreshInterval: 1000, // 1 second
    theme: 'light',
  },
);

export const logStatePart = await appState.getStatePart<ILogState>(
  'logs',
  {
    recentLogs: [],
    isStreaming: false,
    filters: {},
  },
  'soft'
);

export const networkStatePart = await appState.getStatePart<INetworkState>(
  'network',
  {
    connections: [],
    connectionsByIP: {},
    throughputRate: { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
    totalBytes: { in: 0, out: 0 },
    topIPs: [],
    throughputByIP: [],
    throughputHistory: [],
    requestsPerSecond: 0,
    requestsTotal: 0,
    backends: [],
    lastUpdated: 0,
    isLoading: false,
    error: null,
  },
  'soft'
);

export const emailOpsStatePart = await appState.getStatePart<IEmailOpsState>(
  'emailOps',
  {
    emails: [],
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft'
);

export const certificateStatePart = await appState.getStatePart<ICertificateState>(
  'certificates',
  {
    certificates: [],
    summary: { total: 0, valid: 0, expiring: 0, expired: 0, failed: 0, unknown: 0 },
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft'
);

// ============================================================================
// Remote Ingress State
// ============================================================================

export interface IRemoteIngressState {
  edges: interfaces.data.IRemoteIngress[];
  statuses: interfaces.data.IRemoteIngressStatus[];
  selectedEdgeId: string | null;
  newEdgeId: string | null;
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export const remoteIngressStatePart = await appState.getStatePart<IRemoteIngressState>(
  'remoteIngress',
  {
    edges: [],
    statuses: [],
    selectedEdgeId: null,
    newEdgeId: null,
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft'
);

// ============================================================================
// Route Management State
// ============================================================================

export interface IRouteManagementState {
  mergedRoutes: interfaces.data.IMergedRoute[];
  warnings: interfaces.data.IRouteWarning[];
  apiTokens: interfaces.data.IApiTokenInfo[];
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export const routeManagementStatePart = await appState.getStatePart<IRouteManagementState>(
  'routeManagement',
  {
    mergedRoutes: [],
    warnings: [],
    apiTokens: [],
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft'
);

// Actions for state management
interface IActionContext {
  identity: interfaces.data.IIdentity | null;
}

const getActionContext = (): IActionContext => {
  const identity = loginStatePart.getState()!.identity;
  // Treat expired JWTs as no identity — prevents stale persisted sessions from firing requests
  if (identity && identity.expiresAt && identity.expiresAt < Date.now()) {
    return { identity: null };
  }
  return { identity };
};

// Login Action
export const loginAction = loginStatePart.createAction<{
  username: string;
  password: string;
}>(async (statePartArg, dataArg): Promise<ILoginState> => {
  const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_AdminLoginWithUsernameAndPassword
  >('/typedrequest', 'adminLoginWithUsernameAndPassword');

  try {
    const response = await typedRequest.fire({
      username: dataArg.username,
      password: dataArg.password,
    });

    if (response.identity) {
      return {
        identity: response.identity,
        isLoggedIn: true,
      };
    }
    return statePartArg.getState()!;
  } catch (error: unknown) {
    console.error('Login failed:', error);
    return statePartArg.getState()!;
  }
});

// Logout Action — always clears state, even if identity is expired/missing
export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();

  // Try to notify server, but don't block logout if identity is missing/expired
  if (context.identity) {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_AdminLogout
    >('/typedrequest', 'adminLogout');
    try {
      await typedRequest.fire({ identity: context.identity });
    } catch (error) {
      console.error('Logout error:', error);
    }
  }

  // Always clear login state
  return {
    identity: null,
    isLoggedIn: false,
  };
});

// Fetch All Stats Action - Using combined endpoint for efficiency
export const fetchAllStatsAction = statsStatePart.createAction(async (statePartArg): Promise<IStatsState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    // Use combined metrics endpoint - single request instead of 4
    const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetCombinedMetrics
    >('/typedrequest', 'getCombinedMetrics');

    const combinedResponse = await combinedRequest.fire({
      identity: context.identity,
      sections: {
        server: true,
        email: true,
        dns: true,
        security: true,
        network: false, // Network is fetched separately for the network view
      },
    });

    // Update state with all stats from combined response
    return {
      serverStats: combinedResponse.metrics.server || currentState.serverStats,
      emailStats: combinedResponse.metrics.email || currentState.emailStats,
      dnsStats: combinedResponse.metrics.dns || currentState.dnsStats,
      securityMetrics: combinedResponse.metrics.security || currentState.securityMetrics,
      lastUpdated: Date.now(),
      isLoading: false,
      error: null,
    };
  } catch (error: unknown) {
    return {
      ...currentState,
      isLoading: false,
      error: (error as Error).message || 'Failed to fetch statistics',
    };
  }
});

// Fetch Configuration Action (read-only)
export const fetchConfigurationAction = configStatePart.createAction(async (statePartArg): Promise<IConfigState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const configRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetConfiguration
    >('/typedrequest', 'getConfiguration');

    const response = await configRequest.fire({
      identity: context.identity,
    });

    return {
      config: response.config,
      isLoading: false,
      error: null,
    };
  } catch (error: unknown) {
    return {
      ...currentState,
      isLoading: false,
      error: (error as Error).message || 'Failed to fetch configuration',
    };
  }
});

// Fetch Recent Logs Action
export const fetchRecentLogsAction = logStatePart.createAction<{
  limit?: number;
  level?: 'debug' | 'info' | 'warn' | 'error';
  category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
}>(async (statePartArg, dataArg): Promise<ILogState> => {
  const context = getActionContext();
  if (!context.identity) return statePartArg.getState()!;

  const logsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_GetRecentLogs
  >('/typedrequest', 'getRecentLogs');

  const response = await logsRequest.fire({
    identity: context.identity,
    limit: dataArg.limit || 100,
    level: dataArg.level,
    category: dataArg.category,
  });

  return {
    ...statePartArg.getState()!,
    recentLogs: response.logs,
  };
});

// Toggle Auto Refresh Action
export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg): Promise<IUiState> => {
  const currentState = statePartArg.getState()!;
  return {
    ...currentState,
    autoRefresh: !currentState.autoRefresh,
  };
});

// Set Active View Action
export const setActiveViewAction = uiStatePart.createAction<string>(async (statePartArg, viewName): Promise<IUiState> => {
  const currentState = statePartArg.getState()!;

  // If switching to network view, ensure we fetch network data
  if (viewName === 'network' && currentState.activeView !== 'network') {
    setTimeout(() => {
      networkStatePart.dispatchAction(fetchNetworkStatsAction, null);
    }, 100);
  }

  // If switching to certificates view, ensure we fetch certificate data
  if (viewName === 'certificates' && currentState.activeView !== 'certificates') {
    setTimeout(() => {
      certificateStatePart.dispatchAction(fetchCertificateOverviewAction, null);
    }, 100);
  }

  // If switching to routes view, ensure we fetch route data
  if (viewName === 'routes' && currentState.activeView !== 'routes') {
    setTimeout(() => {
      routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
      // Also fetch profiles/targets for the Create Route dropdowns
      profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
    }, 100);
  }

  // If switching to apitokens view, ensure we fetch token data
  if (viewName === 'apitokens' && currentState.activeView !== 'apitokens') {
    setTimeout(() => {
      routeManagementStatePart.dispatchAction(fetchApiTokensAction, null);
    }, 100);
  }

  // If switching to remoteingress view, ensure we fetch edge data
  if (viewName === 'remoteingress' && currentState.activeView !== 'remoteingress') {
    setTimeout(() => {
      remoteIngressStatePart.dispatchAction(fetchRemoteIngressAction, null);
    }, 100);
  }

  // If switching to security profiles or network targets views, fetch profiles/targets data
  if ((viewName === 'securityprofiles' || viewName === 'networktargets') && currentState.activeView !== viewName) {
    setTimeout(() => {
      profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
    }, 100);
  }

  return {
    ...currentState,
    activeView: viewName,
  };
});

// Fetch Network Stats Action
export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg): Promise<INetworkState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    // Fetch active connections using the existing endpoint
    const connectionsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetActiveConnections
    >('/typedrequest', 'getActiveConnections');
    
    const connectionsResponse = await connectionsRequest.fire({
      identity: context.identity,
    });

    // Get network stats for throughput and IP data
    const networkStatsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest(
      '/typedrequest',
      'getNetworkStats'
    );
    
    const networkStatsResponse = await networkStatsRequest.fire({
      identity: context.identity,
    }) as any;

    // Use the connections data for the connection list
    // and network stats for throughput and IP analytics
    const connectionsByIP: { [ip: string]: number } = {};
    
    // Build connectionsByIP from network stats if available
    if (networkStatsResponse.connectionsByIP && Array.isArray(networkStatsResponse.connectionsByIP)) {
      networkStatsResponse.connectionsByIP.forEach((item: { ip: string; count: number }) => {
        connectionsByIP[item.ip] = item.count;
      });
    } else {
      // Fallback: calculate from connections
      connectionsResponse.connections.forEach(conn => {
        const ip = conn.remoteAddress;
        connectionsByIP[ip] = (connectionsByIP[ip] || 0) + 1;
      });
    }

    return {
      connections: connectionsResponse.connections,
      connectionsByIP,
      throughputRate: networkStatsResponse.throughputRate || { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
      totalBytes: networkStatsResponse.totalDataTransferred
        ? { in: networkStatsResponse.totalDataTransferred.bytesIn, out: networkStatsResponse.totalDataTransferred.bytesOut }
        : { in: 0, out: 0 },
      topIPs: networkStatsResponse.topIPs || [],
      throughputByIP: networkStatsResponse.throughputByIP || [],
      throughputHistory: networkStatsResponse.throughputHistory || [],
      requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
      requestsTotal: networkStatsResponse.requestsTotal || 0,
      backends: networkStatsResponse.backends || [],
      lastUpdated: Date.now(),
      isLoading: false,
      error: null,
    };
  } catch (error) {
    console.error('Failed to fetch network stats:', error);
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch network stats',
    };
  }
});

// ============================================================================
// Email Operations Actions
// ============================================================================

// Fetch All Emails Action
export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg): Promise<IEmailOpsState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetAllEmails
    >('/typedrequest', 'getAllEmails');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      emails: response.emails,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch emails',
    };
  }
});

// ============================================================================
// Certificate Actions
// ============================================================================

export const fetchCertificateOverviewAction = certificateStatePart.createAction(async (statePartArg): Promise<ICertificateState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetCertificateOverview
    >('/typedrequest', 'getCertificateOverview');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      certificates: response.certificates,
      summary: response.summary,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch certificate overview',
    };
  }
});

export const reprovisionCertificateAction = certificateStatePart.createAction<string>(
  async (statePartArg, domain, actionContext): Promise<ICertificateState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_ReprovisionCertificateDomain
      >('/typedrequest', 'reprovisionCertificateDomain');

      await request.fire({
        identity: context.identity!,
        domain,
      });

      // Re-fetch overview after reprovisioning
      return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to reprovision certificate',
      };
    }
  }
);

export const deleteCertificateAction = certificateStatePart.createAction<string>(
  async (statePartArg, domain, actionContext): Promise<ICertificateState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteCertificate
      >('/typedrequest', 'deleteCertificate');

      await request.fire({
        identity: context.identity!,
        domain,
      });

      // Re-fetch overview after deletion
      return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to delete certificate',
      };
    }
  }
);

export const importCertificateAction = certificateStatePart.createAction<{
  id: string;
  domainName: string;
  created: number;
  validUntil: number;
  privateKey: string;
  publicKey: string;
  csr: string;
}>(
  async (statePartArg, cert, actionContext): Promise<ICertificateState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_ImportCertificate
      >('/typedrequest', 'importCertificate');

      await request.fire({
        identity: context.identity!,
        cert,
      });

      // Re-fetch overview after import
      return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to import certificate',
      };
    }
  }
);

export async function fetchCertificateExport(domain: string) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_ExportCertificate
  >('/typedrequest', 'exportCertificate');

  return request.fire({
    identity: context.identity!,
    domain,
  });
}

// ============================================================================
// Remote Ingress Standalone Functions
// ============================================================================

export async function fetchConnectionToken(edgeId: string) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_GetRemoteIngressConnectionToken
  >('/typedrequest', 'getRemoteIngressConnectionToken');
  return request.fire({ identity: context.identity!, edgeId });
}

// ============================================================================
// Remote Ingress Actions
// ============================================================================

export const fetchRemoteIngressAction = remoteIngressStatePart.createAction(async (statePartArg): Promise<IRemoteIngressState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const edgesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetRemoteIngresses
    >('/typedrequest', 'getRemoteIngresses');

    const statusRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetRemoteIngressStatus
    >('/typedrequest', 'getRemoteIngressStatus');

    const [edgesResponse, statusResponse] = await Promise.all([
      edgesRequest.fire({ identity: context.identity }),
      statusRequest.fire({ identity: context.identity }),
    ]);

    return {
      ...currentState,
      edges: edgesResponse.edges,
      statuses: statusResponse.statuses,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch remote ingress data',
    };
  }
});

export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
  name: string;
  listenPorts?: number[];
  autoDerivePorts?: boolean;
  tags?: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateRemoteIngress
    >('/typedrequest', 'createRemoteIngress');

    const response = await request.fire({
      identity: context.identity!,
      name: dataArg.name,
      listenPorts: dataArg.listenPorts,
      autoDerivePorts: dataArg.autoDerivePorts,
      tags: dataArg.tags,
    });

    if (response.success) {
      // Refresh the list
      await actionContext!.dispatch(fetchRemoteIngressAction, null);

      return {
        ...statePartArg.getState()!,
        newEdgeId: response.edge.id,
      };
    }

    return currentState;
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to create edge',
    };
  }
});

export const deleteRemoteIngressAction = remoteIngressStatePart.createAction<string>(
  async (statePartArg, edgeId, actionContext): Promise<IRemoteIngressState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteRemoteIngress
      >('/typedrequest', 'deleteRemoteIngress');

      await request.fire({
        identity: context.identity!,
        id: edgeId,
      });

      return await actionContext!.dispatch(fetchRemoteIngressAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to delete edge',
      };
    }
  }
);

export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
  id: string;
  name?: string;
  listenPorts?: number[];
  autoDerivePorts?: boolean;
  tags?: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateRemoteIngress
    >('/typedrequest', 'updateRemoteIngress');

    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      name: dataArg.name,
      listenPorts: dataArg.listenPorts,
      autoDerivePorts: dataArg.autoDerivePorts,
      tags: dataArg.tags,
    });

    return await actionContext!.dispatch(fetchRemoteIngressAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to update edge',
    };
  }
});

export const regenerateRemoteIngressSecretAction = remoteIngressStatePart.createAction<string>(
  async (statePartArg, edgeId): Promise<IRemoteIngressState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_RegenerateRemoteIngressSecret
      >('/typedrequest', 'regenerateRemoteIngressSecret');

      const response = await request.fire({
        identity: context.identity!,
        id: edgeId,
      });

      if (response.success) {
        return {
          ...currentState,
          newEdgeId: edgeId,
        };
      }

      return currentState;
    } catch (error) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to regenerate secret',
      };
    }
  }
);

export const clearNewEdgeIdAction = remoteIngressStatePart.createAction(
  async (statePartArg): Promise<IRemoteIngressState> => {
    return {
      ...statePartArg.getState()!,
      newEdgeId: null,
    };
  }
);

export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
  id: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateRemoteIngress
    >('/typedrequest', 'updateRemoteIngress');

    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      enabled: dataArg.enabled,
    });

    return await actionContext!.dispatch(fetchRemoteIngressAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to toggle edge',
    };
  }
});

// ============================================================================
// VPN State
// ============================================================================

export interface IVpnState {
  clients: interfaces.data.IVpnClient[];
  connectedClients: interfaces.data.IVpnConnectedClient[];
  status: interfaces.data.IVpnServerStatus | null;
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
  /** WireGuard config shown after create/rotate (only shown once) */
  newClientConfig: string | null;
}

export const vpnStatePart = await appState.getStatePart<IVpnState>(
  'vpn',
  {
    clients: [],
    connectedClients: [],
    status: null,
    isLoading: false,
    error: null,
    lastUpdated: 0,
    newClientConfig: null,
  },
  'soft'
);

// ============================================================================
// VPN Actions
// ============================================================================

export const fetchVpnAction = vpnStatePart.createAction(async (statePartArg): Promise<IVpnState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const clientsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetVpnClients
    >('/typedrequest', 'getVpnClients');

    const statusRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetVpnStatus
    >('/typedrequest', 'getVpnStatus');

    const connectedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetVpnConnectedClients
    >('/typedrequest', 'getVpnConnectedClients');

    const [clientsResponse, statusResponse, connectedResponse] = await Promise.all([
      clientsRequest.fire({ identity: context.identity }),
      statusRequest.fire({ identity: context.identity }),
      connectedRequest.fire({ identity: context.identity }),
    ]);

    return {
      ...currentState,
      clients: clientsResponse.clients,
      connectedClients: connectedResponse.connectedClients,
      status: statusResponse.status,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch VPN data',
    };
  }
});

export const createVpnClientAction = vpnStatePart.createAction<{
  clientId: string;
  serverDefinedClientTags?: string[];
  description?: string;
  forceDestinationSmartproxy?: boolean;
  destinationAllowList?: string[];
  destinationBlockList?: string[];
  useHostIp?: boolean;
  useDhcp?: boolean;
  staticIp?: string;
  forceVlan?: boolean;
  vlanId?: number;
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateVpnClient
    >('/typedrequest', 'createVpnClient');

    const response = await request.fire({
      identity: context.identity!,
      clientId: dataArg.clientId,
      serverDefinedClientTags: dataArg.serverDefinedClientTags,
      description: dataArg.description,
      forceDestinationSmartproxy: dataArg.forceDestinationSmartproxy,
      destinationAllowList: dataArg.destinationAllowList,
      destinationBlockList: dataArg.destinationBlockList,
      useHostIp: dataArg.useHostIp,
      useDhcp: dataArg.useDhcp,
      staticIp: dataArg.staticIp,
      forceVlan: dataArg.forceVlan,
      vlanId: dataArg.vlanId,
    });

    if (!response.success) {
      return { ...currentState, error: response.message || 'Failed to create client' };
    }

    const refreshed = await actionContext!.dispatch(fetchVpnAction, null);
    return {
      ...refreshed,
      newClientConfig: response.wireguardConfig || null,
    };
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to create VPN client',
    };
  }
});

export const deleteVpnClientAction = vpnStatePart.createAction<string>(
  async (statePartArg, clientId, actionContext): Promise<IVpnState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteVpnClient
      >('/typedrequest', 'deleteVpnClient');

      await request.fire({ identity: context.identity!, clientId });
      return await actionContext!.dispatch(fetchVpnAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to delete VPN client',
      };
    }
  },
);

export const toggleVpnClientAction = vpnStatePart.createAction<{
  clientId: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const method = dataArg.enabled ? 'enableVpnClient' : 'disableVpnClient';
    type TReq = interfaces.requests.IReq_EnableVpnClient | interfaces.requests.IReq_DisableVpnClient;
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<TReq>(
      '/typedrequest', method,
    );

    await request.fire({ identity: context.identity!, clientId: dataArg.clientId });
    return await actionContext!.dispatch(fetchVpnAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to toggle VPN client',
    };
  }
});

export const updateVpnClientAction = vpnStatePart.createAction<{
  clientId: string;
  description?: string;
  serverDefinedClientTags?: string[];
  forceDestinationSmartproxy?: boolean;
  destinationAllowList?: string[];
  destinationBlockList?: string[];
  useHostIp?: boolean;
  useDhcp?: boolean;
  staticIp?: string;
  forceVlan?: boolean;
  vlanId?: number;
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateVpnClient
    >('/typedrequest', 'updateVpnClient');

    const response = await request.fire({
      identity: context.identity!,
      clientId: dataArg.clientId,
      description: dataArg.description,
      serverDefinedClientTags: dataArg.serverDefinedClientTags,
      forceDestinationSmartproxy: dataArg.forceDestinationSmartproxy,
      destinationAllowList: dataArg.destinationAllowList,
      destinationBlockList: dataArg.destinationBlockList,
      useHostIp: dataArg.useHostIp,
      useDhcp: dataArg.useDhcp,
      staticIp: dataArg.staticIp,
      forceVlan: dataArg.forceVlan,
      vlanId: dataArg.vlanId,
    });

    if (!response.success) {
      return { ...currentState, error: response.message || 'Failed to update client' };
    }

    return await actionContext!.dispatch(fetchVpnAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to update VPN client',
    };
  }
});

export const clearNewClientConfigAction = vpnStatePart.createAction(
  async (statePartArg): Promise<IVpnState> => {
    return { ...statePartArg.getState()!, newClientConfig: null };
  },
);

// ============================================================================
// Security Profiles & Network Targets State
// ============================================================================

export interface IProfilesTargetsState {
  profiles: interfaces.data.ISecurityProfile[];
  targets: interfaces.data.INetworkTarget[];
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export const profilesTargetsStatePart = await appState.getStatePart<IProfilesTargetsState>(
  'profilesTargets',
  {
    profiles: [],
    targets: [],
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft'
);

// ============================================================================
// Security Profiles & Network Targets Actions
// ============================================================================

export const fetchProfilesAndTargetsAction = profilesTargetsStatePart.createAction(
  async (statePartArg): Promise<IProfilesTargetsState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;
    if (!context.identity) return currentState;

    try {
      const profilesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetSecurityProfiles
      >('/typedrequest', 'getSecurityProfiles');

      const targetsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetNetworkTargets
      >('/typedrequest', 'getNetworkTargets');

      const [profilesResponse, targetsResponse] = await Promise.all([
        profilesRequest.fire({ identity: context.identity }),
        targetsRequest.fire({ identity: context.identity }),
      ]);

      return {
        profiles: profilesResponse.profiles,
        targets: targetsResponse.targets,
        isLoading: false,
        error: null,
        lastUpdated: Date.now(),
      };
    } catch (error) {
      return {
        ...currentState,
        isLoading: false,
        error: error instanceof Error ? error.message : 'Failed to fetch profiles/targets',
      };
    }
  }
);

export const createProfileAction = profilesTargetsStatePart.createAction<{
  name: string;
  description?: string;
  security: any;
  extendsProfiles?: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateSecurityProfile
    >('/typedrequest', 'createSecurityProfile');
    await request.fire({
      identity: context.identity!,
      name: dataArg.name,
      description: dataArg.description,
      security: dataArg.security,
      extendsProfiles: dataArg.extendsProfiles,
    });
    return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to create profile',
    };
  }
});

export const updateProfileAction = profilesTargetsStatePart.createAction<{
  id: string;
  name?: string;
  description?: string;
  security?: any;
  extendsProfiles?: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateSecurityProfile
    >('/typedrequest', 'updateSecurityProfile');
    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      name: dataArg.name,
      description: dataArg.description,
      security: dataArg.security,
      extendsProfiles: dataArg.extendsProfiles,
    });
    return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to update profile',
    };
  }
});

export const deleteProfileAction = profilesTargetsStatePart.createAction<{
  id: string;
  force?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_DeleteSecurityProfile
    >('/typedrequest', 'deleteSecurityProfile');
    const response = await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      force: dataArg.force,
    });
    if (!response.success) {
      return {
        ...statePartArg.getState()!,
        error: response.message || 'Failed to delete profile',
      };
    }
    return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to delete profile',
    };
  }
});

export const createTargetAction = profilesTargetsStatePart.createAction<{
  name: string;
  description?: string;
  host: string | string[];
  port: number;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateNetworkTarget
    >('/typedrequest', 'createNetworkTarget');
    await request.fire({
      identity: context.identity!,
      name: dataArg.name,
      description: dataArg.description,
      host: dataArg.host,
      port: dataArg.port,
    });
    return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to create target',
    };
  }
});

export const updateTargetAction = profilesTargetsStatePart.createAction<{
  id: string;
  name?: string;
  description?: string;
  host?: string | string[];
  port?: number;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateNetworkTarget
    >('/typedrequest', 'updateNetworkTarget');
    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      name: dataArg.name,
      description: dataArg.description,
      host: dataArg.host,
      port: dataArg.port,
    });
    return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to update target',
    };
  }
});

export const deleteTargetAction = profilesTargetsStatePart.createAction<{
  id: string;
  force?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_DeleteNetworkTarget
    >('/typedrequest', 'deleteNetworkTarget');
    const response = await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      force: dataArg.force,
    });
    if (!response.success) {
      return {
        ...statePartArg.getState()!,
        error: response.message || 'Failed to delete target',
      };
    }
    return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to delete target',
    };
  }
});

// ============================================================================
// Route Management Actions
// ============================================================================

export const fetchMergedRoutesAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetMergedRoutes
    >('/typedrequest', 'getMergedRoutes');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      ...currentState,
      mergedRoutes: response.routes,
      warnings: response.warnings,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch routes',
    };
  }
});

export const createRouteAction = routeManagementStatePart.createAction<{
  route: any;
  enabled?: boolean;
  metadata?: any;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateRoute
    >('/typedrequest', 'createRoute');

    await request.fire({
      identity: context.identity!,
      route: dataArg.route,
      enabled: dataArg.enabled,
      metadata: dataArg.metadata,
    });

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to create route',
    };
  }
});

export const updateRouteAction = routeManagementStatePart.createAction<{
  id: string;
  route?: any;
  enabled?: boolean;
  metadata?: any;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateRoute
    >('/typedrequest', 'updateRoute');

    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      route: dataArg.route,
      enabled: dataArg.enabled,
      metadata: dataArg.metadata,
    });

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to update route',
    };
  }
});

export const deleteRouteAction = routeManagementStatePart.createAction<string>(
  async (statePartArg, routeId, actionContext): Promise<IRouteManagementState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteRoute
      >('/typedrequest', 'deleteRoute');

      await request.fire({
        identity: context.identity!,
        id: routeId,
      });

      return await actionContext!.dispatch(fetchMergedRoutesAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to delete route',
      };
    }
  }
);

export const toggleRouteAction = routeManagementStatePart.createAction<{
  id: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ToggleRoute
    >('/typedrequest', 'toggleRoute');

    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      enabled: dataArg.enabled,
    });

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to toggle route',
    };
  }
});

export const setRouteOverrideAction = routeManagementStatePart.createAction<{
  routeName: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_SetRouteOverride
    >('/typedrequest', 'setRouteOverride');

    await request.fire({
      identity: context.identity!,
      routeName: dataArg.routeName,
      enabled: dataArg.enabled,
    });

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to set override',
    };
  }
});

export const removeRouteOverrideAction = routeManagementStatePart.createAction<string>(
  async (statePartArg, routeName, actionContext): Promise<IRouteManagementState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_RemoveRouteOverride
      >('/typedrequest', 'removeRouteOverride');

      await request.fire({
        identity: context.identity!,
        routeName,
      });

      return await actionContext!.dispatch(fetchMergedRoutesAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to remove override',
      };
    }
  }
);

// ============================================================================
// API Token Actions
// ============================================================================

export const fetchApiTokensAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListApiTokens
    >('/typedrequest', 'listApiTokens');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      ...currentState,
      apiTokens: response.tokens,
    };
  } catch (error) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to fetch tokens',
    };
  }
});

export async function createApiToken(name: string, scopes: interfaces.data.TApiTokenScope[], expiresInDays?: number | null) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_CreateApiToken
  >('/typedrequest', 'createApiToken');

  return request.fire({
    identity: context.identity!,
    name,
    scopes,
    expiresInDays,
  });
}

export async function rollApiToken(id: string) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_RollApiToken
  >('/typedrequest', 'rollApiToken');

  return request.fire({
    identity: context.identity!,
    id,
  });
}

export const revokeApiTokenAction = routeManagementStatePart.createAction<string>(
  async (statePartArg, tokenId, actionContext): Promise<IRouteManagementState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_RevokeApiToken
      >('/typedrequest', 'revokeApiToken');

      await request.fire({
        identity: context.identity!,
        id: tokenId,
      });

      return await actionContext!.dispatch(fetchApiTokensAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to revoke token',
      };
    }
  }
);

export const toggleApiTokenAction = routeManagementStatePart.createAction<{
  id: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ToggleApiToken
    >('/typedrequest', 'toggleApiToken');

    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      enabled: dataArg.enabled,
    });

    return await actionContext!.dispatch(fetchApiTokensAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to toggle token',
    };
  }
});

// ============================================================================
// TypedSocket Client for Real-time Log Streaming
// ============================================================================

let socketClient: plugins.typedsocket.TypedSocket | null = null;
const socketRouter = new plugins.domtools.plugins.typedrequest.TypedRouter();

// Batched log entry handler — buffers incoming entries and flushes once per animation frame
let logEntryBuffer: interfaces.data.ILogEntry[] = [];
let logFlushScheduled = false;

function flushLogEntries() {
  logFlushScheduled = false;
  if (logEntryBuffer.length === 0) return;
  const current = logStatePart.getState()!;
  const updated = [...current.recentLogs, ...logEntryBuffer];
  logEntryBuffer = [];
  // Cap at 2000 entries
  if (updated.length > 2000) {
    updated.splice(0, updated.length - 2000);
  }
  logStatePart.setState({ ...current, recentLogs: updated } as ILogState);
}

// Register handler for pushed log entries from the server
socketRouter.addTypedHandler(
  new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushLogEntry>(
    'pushLogEntry',
    async (dataArg) => {
      logEntryBuffer.push(dataArg.entry);
      if (!logFlushScheduled) {
        logFlushScheduled = true;
        requestAnimationFrame(flushLogEntries);
      }
      return {};
    }
  )
);

async function connectSocket() {
  if (socketClient) return;
  try {
    socketClient = await plugins.typedsocket.TypedSocket.createClient(
      socketRouter,
      plugins.typedsocket.TypedSocket.useWindowLocationOriginUrl(),
    );
    await socketClient.setTag('role', 'ops_dashboard');
  } catch (err) {
    console.error('TypedSocket connection failed:', err);
    socketClient = null;
  }
}

async function disconnectSocket() {
  if (socketClient) {
    try {
      await socketClient.stop();
    } catch {
      // ignore disconnect errors
    }
    socketClient = null;
  }
}

// In-flight guard to prevent concurrent refresh requests
let isRefreshing = false;

// Combined refresh action for efficient polling
async function dispatchCombinedRefreshAction() {
  if (isRefreshing) return;
  isRefreshing = true;
  try {
    await dispatchCombinedRefreshActionInner();
  } finally {
    isRefreshing = false;
  }
}

async function dispatchCombinedRefreshActionInner() {
  const context = getActionContext();
  if (!context.identity) return;
  const currentView = uiStatePart.getState()!.activeView;

  try {
    // Always fetch basic stats for dashboard widgets
    const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetCombinedMetrics
    >('/typedrequest', 'getCombinedMetrics');

    const combinedResponse = await combinedRequest.fire({
      identity: context.identity,
      sections: {
        server: true,
        email: true,
        dns: true,
        security: true,
        network: currentView === 'network', // Only fetch network if on network view
      },
    });

    // Update all stats from combined response
    const currentStatsState = statsStatePart.getState()!;
    statsStatePart.setState({
      ...currentStatsState,
      serverStats: combinedResponse.metrics.server || currentStatsState.serverStats,
      emailStats: combinedResponse.metrics.email || currentStatsState.emailStats,
      dnsStats: combinedResponse.metrics.dns || currentStatsState.dnsStats,
      securityMetrics: combinedResponse.metrics.security || currentStatsState.securityMetrics,
      lastUpdated: Date.now(),
      isLoading: false,
      error: null,
    });

    // Update network stats if included
    if (combinedResponse.metrics.network && currentView === 'network') {
      const network = combinedResponse.metrics.network;
      const connectionsByIP: { [ip: string]: number } = {};
      
      // Convert connection details to IP counts
      network.connectionDetails.forEach(conn => {
        connectionsByIP[conn.remoteAddress] = (connectionsByIP[conn.remoteAddress] || 0) + 1;
      });

      // Fetch detailed connections for the network view
      try {
        const connectionsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
          interfaces.requests.IReq_GetActiveConnections
        >('/typedrequest', 'getActiveConnections');
        
        const connectionsResponse = await connectionsRequest.fire({
          identity: context.identity,
        });

        networkStatePart.setState({
          ...networkStatePart.getState()!,
          connections: connectionsResponse.connections,
          connectionsByIP,
          throughputRate: {
            bytesInPerSecond: network.totalBandwidth.in,
            bytesOutPerSecond: network.totalBandwidth.out
          },
          totalBytes: network.totalBytes || { in: 0, out: 0 },
          topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.requests })),
          throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
          throughputHistory: network.throughputHistory || [],
          requestsPerSecond: network.requestsPerSecond || 0,
          requestsTotal: network.requestsTotal || 0,
          backends: network.backends || [],
          lastUpdated: Date.now(),
          isLoading: false,
          error: null,
        });
      } catch (error: unknown) {
        console.error('Failed to fetch connections:', error);
        networkStatePart.setState({
          ...networkStatePart.getState()!,
          connections: [],
          connectionsByIP,
          throughputRate: {
            bytesInPerSecond: network.totalBandwidth.in,
            bytesOutPerSecond: network.totalBandwidth.out
          },
          totalBytes: network.totalBytes || { in: 0, out: 0 },
          topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.requests })),
          throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
          throughputHistory: network.throughputHistory || [],
          requestsPerSecond: network.requestsPerSecond || 0,
          requestsTotal: network.requestsTotal || 0,
          backends: network.backends || [],
          lastUpdated: Date.now(),
          isLoading: false,
          error: null,
        });
      }
    }

    // Refresh certificate data if on certificates view
    if (currentView === 'certificates') {
      try {
        await certificateStatePart.dispatchAction(fetchCertificateOverviewAction, null);
      } catch (error) {
        console.error('Certificate refresh failed:', error);
      }
    }

    // Refresh remote ingress data if on remoteingress view
    if (currentView === 'remoteingress') {
      try {
        await remoteIngressStatePart.dispatchAction(fetchRemoteIngressAction, null);
      } catch (error) {
        console.error('Remote ingress refresh failed:', error);
      }
    }

    // Refresh VPN data if on vpn view
    if (currentView === 'vpn') {
      try {
        await vpnStatePart.dispatchAction(fetchVpnAction, null);
      } catch (error) {
        console.error('VPN refresh failed:', error);
      }
    }
  } catch (error) {
    console.error('Combined refresh failed:', error);
    // If the error looks like an auth failure (invalid JWT), force re-login
    const errMsg = String(error);
    if (errMsg.includes('invalid') || errMsg.includes('unauthorized') || errMsg.includes('401')) {
      await loginStatePart.dispatchAction(logoutAction, null);
      window.location.reload();
    }
  }
}

// Create a proper action for the combined refresh so we can use createScheduledAction
const combinedRefreshAction = statsStatePart.createAction<void>(async (statePartArg) => {
  await dispatchCombinedRefreshAction();
  // Return current state — dispatchCombinedRefreshAction already updates all state parts directly
  return statePartArg.getState()!;
});

// Scheduled refresh process with autoPause: 'visibility' — automatically pauses when tab is hidden
let refreshProcess: ReturnType<typeof statsStatePart.createScheduledAction> | null = null;

const startAutoRefresh = () => {
  const uiState = uiStatePart.getState()!;
  const loginState = loginStatePart.getState()!;

  if (uiState.autoRefresh && loginState.isLoggedIn) {
    // Dispose old process if interval changed or not running
    if (refreshProcess) {
      refreshProcess.dispose();
      refreshProcess = null;
    }
    refreshProcess = statsStatePart.createScheduledAction({
      action: combinedRefreshAction,
      payload: undefined,
      intervalMs: uiState.refreshInterval,
      autoPause: 'visibility',
    });
  } else {
    if (refreshProcess) {
      refreshProcess.dispose();
      refreshProcess = null;
    }
  }
};

// Watch for relevant changes
let previousAutoRefresh = uiStatePart.getState()!.autoRefresh;
let previousRefreshInterval = uiStatePart.getState()!.refreshInterval;
let previousIsLoggedIn = loginStatePart.getState()!.isLoggedIn;

uiStatePart.select((s) => ({ autoRefresh: s.autoRefresh, refreshInterval: s.refreshInterval }))
  .subscribe((state) => {
    if (state.autoRefresh !== previousAutoRefresh ||
        state.refreshInterval !== previousRefreshInterval) {
      previousAutoRefresh = state.autoRefresh;
      previousRefreshInterval = state.refreshInterval;
      startAutoRefresh();
    }
  });

loginStatePart.select((s) => s.isLoggedIn).subscribe((isLoggedIn) => {
  if (isLoggedIn !== previousIsLoggedIn) {
    previousIsLoggedIn = isLoggedIn;
    startAutoRefresh();

    // Connect/disconnect TypedSocket based on login state
    if (isLoggedIn) {
      connectSocket();
    } else {
      disconnectSocket();
    }
  }
});

// Pause/resume WebSocket when tab visibility changes
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    disconnectSocket();
  } else if (loginStatePart.getState()!.isLoggedIn) {
    connectSocket();
  }
});

// Initial start
startAutoRefresh();

// Connect TypedSocket if already logged in (e.g., persistent session)
if (loginStatePart.getState()!.isLoggedIn) {
  connectSocket();
}