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

// ============================================================================
// Smartstate instance
// ============================================================================
export const appState = new plugins.domtools.plugins.smartstate.Smartstate();

// ============================================================================
// State Part Interfaces
// ============================================================================

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

export interface ISystemState {
  status: interfaces.data.ISystemStatus | null;
}

export interface IServicesState {
  services: interfaces.data.IService[];
  currentService: interfaces.data.IService | null;
  currentHostedAppLifecycle: interfaces.data.IHostedAppLifecycleState | null;
  currentServiceLogs: interfaces.data.ILogEntry[];
  currentServiceStats: interfaces.data.IContainerStats | null;
  platformServices: interfaces.data.IPlatformService[];
  currentPlatformService: interfaces.data.IPlatformService | null;
  currentPlatformServiceStats: interfaces.data.IContainerStats | null;
  currentPlatformServiceLogs: interfaces.data.ILogEntry[];
}

export interface INetworkState {
  targets: interfaces.data.INetworkTarget[];
  stats: interfaces.data.INetworkStats | null;
  trafficStats: interfaces.data.ITrafficStats | null;
  dnsRecords: interfaces.data.IDnsRecord[];
  domains: interfaces.data.IDomainDetail[];
  gatewayDomains: interfaces.data.IGatewayDomain[];
  gatewayDnsRecords: interfaces.data.IGatewayDnsRecord[];
  certificates: interfaces.data.ICertificate[];
}

export interface IRegistriesState {
  tokens: interfaces.data.IRegistryToken[];
  registryStatus: { running: boolean; port: number } | null;
}

export interface IBackupsState {
  backups: interfaces.data.IBackup[];
  schedules: interfaces.data.IBackupSchedule[];
}

export interface ISettingsState {
  settings: interfaces.data.ISettings | null;
  backupPasswordConfigured: boolean;
  managedDcRouterStatus: interfaces.data.IManagedDcRouterStatus | null;
}

export interface IAppStoreState {
  apps: interfaces.requests.IAppStoreApp[];
  upgradeableServices: interfaces.requests.IUpgradeableAppStoreService[];
  upgradeOperations: interfaces.requests.IAppStoreUpgradeOperation[];
}

export interface IUiState {
  activeView: string;
  activeSubview: string | null;
  autoRefresh: boolean;
  refreshInterval: number;
  pendingAppTemplate?: any;
}

// ============================================================================
// State Parts
// ============================================================================

export const loginStatePart = await appState.getStatePart<ILoginState>(
  'login',
  {
    identity: null,
    isLoggedIn: false,
  },
  'persistent',
);

export const systemStatePart = await appState.getStatePart<ISystemState>(
  'system',
  {
    status: null,
  },
  'soft',
);

export const servicesStatePart = await appState.getStatePart<IServicesState>(
  'services',
  {
    services: [],
    currentService: null,
    currentHostedAppLifecycle: null,
    currentServiceLogs: [],
    currentServiceStats: null,
    platformServices: [],
    currentPlatformService: null,
    currentPlatformServiceStats: null,
    currentPlatformServiceLogs: [],
  },
  'soft',
);

export const networkStatePart = await appState.getStatePart<INetworkState>(
  'network',
  {
    targets: [],
    stats: null,
    trafficStats: null,
    dnsRecords: [],
    domains: [],
    gatewayDomains: [],
    gatewayDnsRecords: [],
    certificates: [],
  },
  'soft',
);

export const registriesStatePart = await appState.getStatePart<IRegistriesState>(
  'registries',
  {
    tokens: [],
    registryStatus: null,
  },
  'soft',
);

export const backupsStatePart = await appState.getStatePart<IBackupsState>(
  'backups',
  {
    backups: [],
    schedules: [],
  },
  'soft',
);

export const settingsStatePart = await appState.getStatePart<ISettingsState>(
  'settings',
  {
    settings: null,
    backupPasswordConfigured: false,
    managedDcRouterStatus: null,
  },
  'soft',
);

export const appStoreStatePart = await appState.getStatePart<IAppStoreState>(
  'appStore',
  {
    apps: [],
    upgradeableServices: [],
    upgradeOperations: [],
  },
  'soft',
);

export const uiStatePart = await appState.getStatePart<IUiState>(
  'ui',
  {
    activeView: 'dashboard',
    activeSubview: null,
    autoRefresh: true,
    refreshInterval: 30000,
  },
);

// ============================================================================
// Helpers
// ============================================================================

interface IActionContext {
  identity: interfaces.data.IIdentity | null;
}

const getActionContext = (): IActionContext => {
  return { identity: loginStatePart.getState().identity };
};

// ============================================================================
// Login Actions
// ============================================================================

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

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

    return {
      identity: response.identity,
      isLoggedIn: true,
    };
  } catch (err) {
    console.error('Login failed:', err);
    return { identity: null, isLoggedIn: false };
  }
});

export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    if (context.identity) {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_AdminLogout
      >('/typedrequest', 'adminLogout');
      await typedRequest.fire({ identity: context.identity });
    }
  } catch (err) {
    console.error('Logout error:', err);
  }
  return { identity: null, isLoggedIn: false };
});

// ============================================================================
// System Status Actions
// ============================================================================

export const fetchSystemStatusAction = systemStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetSystemStatus
    >('/typedrequest', 'getSystemStatus');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { status: response.status };
  } catch (err) {
    console.error('Failed to fetch system status:', err);
    return statePartArg.getState();
  }
});

// ============================================================================
// Services Actions
// ============================================================================

export const fetchServicesAction = servicesStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetServices
    >('/typedrequest', 'getServices');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), services: response.services };
  } catch (err) {
    console.error('Failed to fetch services:', err);
    return statePartArg.getState();
  }
});

export const fetchServiceAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetService
      >('/typedrequest', 'getService');
      const hostedAppRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetHostedAppLifecycleForService
      >('/typedrequest', 'getHostedAppLifecycleForService');
      const [response, hostedAppResponse] = await Promise.all([
        typedRequest.fire({
          identity: context.identity!,
          serviceName: dataArg.name,
        }),
        hostedAppRequest.fire({
          identity: context.identity!,
          serviceName: dataArg.name,
        }),
      ]);
      return {
        ...statePartArg.getState(),
        currentService: response.service,
        currentHostedAppLifecycle: hostedAppResponse.state || null,
      };
    } catch (err) {
      console.error('Failed to fetch service:', err);
      return statePartArg.getState();
    }
  },
);

export const createServiceAction = servicesStatePart.createAction<{
  config: interfaces.data.IServiceCreate;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateService
    >('/typedrequest', 'createService');
    await typedRequest.fire({
      identity: context.identity!,
      serviceConfig: dataArg.config,
    });
    // Re-fetch services list
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetServices
    >('/typedrequest', 'getServices');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), services: listResp.services };
  } catch (err) {
    console.error('Failed to create service:', err);
    return statePartArg.getState();
  }
});

export const deleteServiceAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteService
      >('/typedrequest', 'deleteService');
      await typedRequest.fire({
        identity: context.identity!,
        serviceName: dataArg.name,
      });
      const state = statePartArg.getState();
      return {
        ...state,
        services: state.services.filter((s) => s.name !== dataArg.name),
        currentService: null,
        currentHostedAppLifecycle: null,
      };
    } catch (err) {
      console.error('Failed to delete service:', err);
      return statePartArg.getState();
    }
  },
);

export const dismissHostedAppBootstrapAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DismissHostedAppBootstrapAction
      >('/typedrequest', 'dismissHostedAppBootstrapAction');
      const response = await typedRequest.fire({
        identity: context.identity!,
        serviceName: dataArg.name,
      });
      return {
        ...statePartArg.getState(),
        currentHostedAppLifecycle: response.state || null,
      };
    } catch (err) {
      console.error('Failed to dismiss hosted app bootstrap action:', err);
      return statePartArg.getState();
    }
  },
);

export const startServiceAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_StartService
      >('/typedrequest', 'startService');
      await typedRequest.fire({
        identity: context.identity!,
        serviceName: dataArg.name,
      });
      // Re-fetch services
      const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetServices
      >('/typedrequest', 'getServices');
      const listResp = await listReq.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), services: listResp.services };
    } catch (err) {
      console.error('Failed to start service:', err);
      return statePartArg.getState();
    }
  },
);

export const stopServiceAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_StopService
      >('/typedrequest', 'stopService');
      await typedRequest.fire({
        identity: context.identity!,
        serviceName: dataArg.name,
      });
      const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetServices
      >('/typedrequest', 'getServices');
      const listResp = await listReq.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), services: listResp.services };
    } catch (err) {
      console.error('Failed to stop service:', err);
      return statePartArg.getState();
    }
  },
);

export const restartServiceAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_RestartService
      >('/typedrequest', 'restartService');
      await typedRequest.fire({
        identity: context.identity!,
        serviceName: dataArg.name,
      });
      const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetServices
      >('/typedrequest', 'getServices');
      const listResp = await listReq.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), services: listResp.services };
    } catch (err) {
      console.error('Failed to restart service:', err);
      return statePartArg.getState();
    }
  },
);

export const fetchServiceLogsAction = servicesStatePart.createAction<{
  name: string;
  lines?: number;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetServiceLogs
    >('/typedrequest', 'getServiceLogs');
    const response = await typedRequest.fire({
      identity: context.identity!,
      serviceName: dataArg.name,
      tail: dataArg.lines || 200,
    });
    return { ...statePartArg.getState(), currentServiceLogs: response.logs };
  } catch (err) {
    console.error('Failed to fetch service logs:', err);
    return statePartArg.getState();
  }
});

export const fetchServiceStatsAction = servicesStatePart.createAction<{ name: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetServiceStats
      >('/typedrequest', 'getServiceStats');
      const response = await typedRequest.fire({
        identity: context.identity!,
        serviceName: dataArg.name,
      });
      return { ...statePartArg.getState(), currentServiceStats: response.stats };
    } catch (err) {
      console.error('Failed to fetch service stats:', err);
      return statePartArg.getState();
    }
  },
);

// ============================================================================
// Platform Services Actions
// ============================================================================

export const fetchPlatformServicesAction = servicesStatePart.createAction(
  async (statePartArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetPlatformServices
      >('/typedrequest', 'getPlatformServices');
      const response = await typedRequest.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), platformServices: response.platformServices };
    } catch (err) {
      console.error('Failed to fetch platform services:', err);
      return statePartArg.getState();
    }
  },
);

export const startPlatformServiceAction = servicesStatePart.createAction<{
  serviceType: interfaces.data.TPlatformServiceType;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_StartPlatformService
    >('/typedrequest', 'startPlatformService');
    await typedRequest.fire({
      identity: context.identity!,
      serviceType: dataArg.serviceType,
    });
    // Re-fetch platform services
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetPlatformServices
    >('/typedrequest', 'getPlatformServices');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), platformServices: listResp.platformServices };
  } catch (err) {
    console.error('Failed to start platform service:', err);
    return statePartArg.getState();
  }
});

export const stopPlatformServiceAction = servicesStatePart.createAction<{
  serviceType: interfaces.data.TPlatformServiceType;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_StopPlatformService
    >('/typedrequest', 'stopPlatformService');
    await typedRequest.fire({
      identity: context.identity!,
      serviceType: dataArg.serviceType,
    });
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetPlatformServices
    >('/typedrequest', 'getPlatformServices');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), platformServices: listResp.platformServices };
  } catch (err) {
    console.error('Failed to stop platform service:', err);
    return statePartArg.getState();
  }
});

export const fetchPlatformServiceStatsAction = servicesStatePart.createAction<{
  serviceType: interfaces.data.TPlatformServiceType;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetPlatformServiceStats
    >('/typedrequest', 'getPlatformServiceStats');
    const response = await typedRequest.fire({
      identity: context.identity!,
      serviceType: dataArg.serviceType,
    });
    return { ...statePartArg.getState(), currentPlatformServiceStats: response.stats };
  } catch (err) {
    console.error('Failed to fetch platform service stats:', err);
    return { ...statePartArg.getState(), currentPlatformServiceStats: null };
  }
});

export const fetchPlatformServiceLogsAction = servicesStatePart.createAction<{
  serviceType: interfaces.data.TPlatformServiceType;
  tail?: number;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetPlatformServiceLogs
    >('/typedrequest', 'getPlatformServiceLogs');
    const response = await typedRequest.fire({
      identity: context.identity!,
      serviceType: dataArg.serviceType,
      tail: dataArg.tail || 100,
    });
    return { ...statePartArg.getState(), currentPlatformServiceLogs: response.logs };
  } catch (err) {
    console.error('Failed to fetch platform service logs:', err);
    return { ...statePartArg.getState(), currentPlatformServiceLogs: [] };
  }
});

// ============================================================================
// Network Actions
// ============================================================================

export const fetchNetworkTargetsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetNetworkTargets
    >('/typedrequest', 'getNetworkTargets');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), targets: response.targets };
  } catch (err) {
    console.error('Failed to fetch network targets:', err);
    return statePartArg.getState();
  }
});

export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetNetworkStats
    >('/typedrequest', 'getNetworkStats');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), stats: response.stats };
  } catch (err) {
    console.error('Failed to fetch network stats:', err);
    return statePartArg.getState();
  }
});

export const fetchTrafficStatsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetTrafficStats
    >('/typedrequest', 'getTrafficStats');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), trafficStats: response.stats };
  } catch (err) {
    console.error('Failed to fetch traffic stats:', err);
    return statePartArg.getState();
  }
});

export const fetchDnsRecordsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetDnsRecords
    >('/typedrequest', 'getDnsRecords');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), dnsRecords: response.records };
  } catch (err) {
    console.error('Failed to fetch DNS records:', err);
    return statePartArg.getState();
  }
});

export const syncDnsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_SyncDns
    >('/typedrequest', 'syncDns');
    await typedRequest.fire({ identity: context.identity! });
    // Re-fetch DNS records
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetDnsRecords
    >('/typedrequest', 'getDnsRecords');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), dnsRecords: listResp.records };
  } catch (err) {
    console.error('Failed to sync DNS:', err);
    return statePartArg.getState();
  }
});

export const fetchDomainsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetDomains
    >('/typedrequest', 'getDomains');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), domains: response.domains };
  } catch (err) {
    console.error('Failed to fetch domains:', err);
    return statePartArg.getState();
  }
});

export const fetchGatewayDomainsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetGatewayDomains
    >('/typedrequest', 'getGatewayDomains');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), gatewayDomains: response.domains };
  } catch (err) {
    console.error('Failed to fetch gateway domains:', err);
    return statePartArg.getState();
  }
});

export const fetchGatewayDnsRecordsAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetGatewayDnsRecords
    >('/typedrequest', 'getGatewayDnsRecords');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), gatewayDnsRecords: response.records };
  } catch (err) {
    console.error('Failed to fetch gateway DNS records:', err);
    return statePartArg.getState();
  }
});

export const fetchCertificatesAction = networkStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListCertificates
    >('/typedrequest', 'listCertificates');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), certificates: response.certificates };
  } catch (err) {
    console.error('Failed to fetch certificates:', err);
    return statePartArg.getState();
  }
});

export const renewCertificateAction = networkStatePart.createAction<{ domain: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_RenewCertificate
      >('/typedrequest', 'renewCertificate');
      await typedRequest.fire({
        identity: context.identity!,
        domain: dataArg.domain,
      });
      // Re-fetch certificates
      const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_ListCertificates
      >('/typedrequest', 'listCertificates');
      const listResp = await listReq.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), certificates: listResp.certificates };
    } catch (err) {
      console.error('Failed to renew certificate:', err);
      return statePartArg.getState();
    }
  },
);

// ============================================================================
// Registry Actions
// ============================================================================

export const fetchRegistryTokensAction = registriesStatePart.createAction(
  async (statePartArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetRegistryTokens
      >('/typedrequest', 'getRegistryTokens');
      const response = await typedRequest.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), tokens: response.tokens };
    } catch (err) {
      console.error('Failed to fetch registry tokens:', err);
      return statePartArg.getState();
    }
  },
);

export const createRegistryTokenAction = registriesStatePart.createAction<{
  token: interfaces.data.ICreateTokenRequest;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateRegistryToken
    >('/typedrequest', 'createRegistryToken');
    await typedRequest.fire({
      identity: context.identity!,
      token: dataArg.token,
    });
    // Re-fetch tokens
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetRegistryTokens
    >('/typedrequest', 'getRegistryTokens');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), tokens: listResp.tokens };
  } catch (err) {
    console.error('Failed to create registry token:', err);
    return statePartArg.getState();
  }
});

export const deleteRegistryTokenAction = registriesStatePart.createAction<{
  tokenId: string;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_DeleteRegistryToken
    >('/typedrequest', 'deleteRegistryToken');
    await typedRequest.fire({
      identity: context.identity!,
      tokenId: dataArg.tokenId,
    });
    const state = statePartArg.getState();
    return {
      ...state,
      tokens: state.tokens.filter((t) => t.id !== dataArg.tokenId),
    };
  } catch (err) {
    console.error('Failed to delete registry token:', err);
    return statePartArg.getState();
  }
});

// ============================================================================
// Backups Actions
// ============================================================================

export const fetchBackupsAction = backupsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetBackups
    >('/typedrequest', 'getBackups');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), backups: response.backups };
  } catch (err) {
    console.error('Failed to fetch backups:', err);
    return statePartArg.getState();
  }
});

export const deleteBackupAction = backupsStatePart.createAction<{ backupId: number }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteBackup
      >('/typedrequest', 'deleteBackup');
      await typedRequest.fire({
        identity: context.identity!,
        backupId: dataArg.backupId,
      });
      const state = statePartArg.getState();
      return {
        ...state,
        backups: state.backups.filter((b) => b.id !== dataArg.backupId),
      };
    } catch (err) {
      console.error('Failed to delete backup:', err);
      return statePartArg.getState();
    }
  },
);

export const fetchSchedulesAction = backupsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetBackupSchedules
    >('/typedrequest', 'getBackupSchedules');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), schedules: response.schedules };
  } catch (err) {
    console.error('Failed to fetch schedules:', err);
    return statePartArg.getState();
  }
});

export const createScheduleAction = backupsStatePart.createAction<{
  config: interfaces.data.IBackupScheduleCreate;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateBackupSchedule
    >('/typedrequest', 'createBackupSchedule');
    await typedRequest.fire({
      identity: context.identity!,
      scheduleConfig: dataArg.config,
    });
    // Re-fetch schedules
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetBackupSchedules
    >('/typedrequest', 'getBackupSchedules');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { ...statePartArg.getState(), schedules: listResp.schedules };
  } catch (err) {
    console.error('Failed to create schedule:', err);
    return statePartArg.getState();
  }
});

export const deleteScheduleAction = backupsStatePart.createAction<{ scheduleId: number }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteBackupSchedule
      >('/typedrequest', 'deleteBackupSchedule');
      await typedRequest.fire({
        identity: context.identity!,
        scheduleId: dataArg.scheduleId,
      });
      const state = statePartArg.getState();
      return {
        ...state,
        schedules: state.schedules.filter((s) => s.id !== dataArg.scheduleId),
      };
    } catch (err) {
      console.error('Failed to delete schedule:', err);
      return statePartArg.getState();
    }
  },
);

export const triggerScheduleAction = backupsStatePart.createAction<{ scheduleId: number }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_TriggerBackupSchedule
      >('/typedrequest', 'triggerBackupSchedule');
      await typedRequest.fire({
        identity: context.identity!,
        scheduleId: dataArg.scheduleId,
      });
      // Re-fetch backups
      const backupsReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetBackups
      >('/typedrequest', 'getBackups');
      const backupsResp = await backupsReq.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), backups: backupsResp.backups };
    } catch (err) {
      console.error('Failed to trigger schedule:', err);
      return statePartArg.getState();
    }
  },
);

// ============================================================================
// Settings Actions
// ============================================================================

export const fetchSettingsAction = settingsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const [settingsResp, passwordResp, managedDcRouterResp] = await Promise.all([
      new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetSettings
      >('/typedrequest', 'getSettings').fire({ identity: context.identity! }),
      new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetBackupPasswordStatus
      >('/typedrequest', 'getBackupPasswordStatus').fire({ identity: context.identity! }),
      new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetManagedDcRouterStatus
      >('/typedrequest', 'getManagedDcRouterStatus').fire({ identity: context.identity! }),
    ]);
    return {
      settings: settingsResp.settings,
      backupPasswordConfigured: passwordResp.status.isConfigured,
      managedDcRouterStatus: managedDcRouterResp.status,
    };
  } catch (err) {
    console.error('Failed to fetch settings:', err);
    return statePartArg.getState();
  }
});

export const updateSettingsAction = settingsStatePart.createAction<{
  settings: Partial<interfaces.data.ISettings>;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateSettings
    >('/typedrequest', 'updateSettings');
    const response = await typedRequest.fire({
      identity: context.identity!,
      settings: dataArg.settings,
    });
    return { ...statePartArg.getState(), settings: response.settings };
  } catch (err) {
    console.error('Failed to update settings:', err);
    return statePartArg.getState();
  }
});

export const fetchManagedDcRouterStatusAction = settingsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const response = await new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetManagedDcRouterStatus
    >('/typedrequest', 'getManagedDcRouterStatus').fire({ identity: context.identity! });
    return { ...statePartArg.getState(), managedDcRouterStatus: response.status };
  } catch (err) {
    console.error('Failed to fetch managed dcrouter status:', err);
    return statePartArg.getState();
  }
});

export const startManagedDcRouterAction = settingsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const response = await new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_StartManagedDcRouter
    >('/typedrequest', 'startManagedDcRouter').fire({ identity: context.identity! });
    return { ...statePartArg.getState(), managedDcRouterStatus: response.status };
  } catch (err) {
    console.error('Failed to start managed dcrouter:', err);
    return statePartArg.getState();
  }
});

export const stopManagedDcRouterAction = settingsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const response = await new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_StopManagedDcRouter
    >('/typedrequest', 'stopManagedDcRouter').fire({ identity: context.identity! });
    return { ...statePartArg.getState(), managedDcRouterStatus: response.status };
  } catch (err) {
    console.error('Failed to stop managed dcrouter:', err);
    return statePartArg.getState();
  }
});

export const restartManagedDcRouterAction = settingsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const response = await new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_RestartManagedDcRouter
    >('/typedrequest', 'restartManagedDcRouter').fire({ identity: context.identity! });
    return { ...statePartArg.getState(), managedDcRouterStatus: response.status };
  } catch (err) {
    console.error('Failed to restart managed dcrouter:', err);
    return statePartArg.getState();
  }
});

export const setBackupPasswordAction = settingsStatePart.createAction<{ password: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_SetBackupPassword
      >('/typedrequest', 'setBackupPassword');
      await typedRequest.fire({
        identity: context.identity!,
        password: dataArg.password,
      });
      return { ...statePartArg.getState(), backupPasswordConfigured: true };
    } catch (err) {
      console.error('Failed to set backup password:', err);
      return statePartArg.getState();
    }
  },
);

// ============================================================================
// UI Actions
// ============================================================================

export const setActiveViewAction = uiStatePart.createAction<{ view: string; subview?: string | null }>(
  async (statePartArg, dataArg) => {
    const normalizedView = dataArg.view.toLowerCase().replace(/\s+/g, '-');
    const normalizedSubview = dataArg.subview
      ? dataArg.subview.toLowerCase().replace(/\s+/g, '-')
      : null;
    return {
      ...statePartArg.getState(),
      activeView: normalizedView,
      activeSubview: normalizedSubview,
    };
  },
);

export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg) => {
  const state = statePartArg.getState();
  return { ...state, autoRefresh: !state.autoRefresh };
});

// ============================================================================
// Auto-refresh system
// ============================================================================

let refreshIntervalHandle: ReturnType<typeof setInterval> | null = null;

const dispatchCombinedRefreshAction = async () => {
  const loginState = loginStatePart.getState();
  if (!loginState.isLoggedIn) return;

  try {
    await Promise.all([
      systemStatePart.dispatchAction(fetchSystemStatusAction, null),
      networkStatePart.dispatchAction(fetchTrafficStatsAction, null),
    ]);
  } catch (err) {
    // Silently fail on auto-refresh
  }
};

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

  if (uiState.autoRefresh && loginState.isLoggedIn) {
    if (refreshIntervalHandle) {
      clearInterval(refreshIntervalHandle);
    }
    refreshIntervalHandle = setInterval(() => {
      dispatchCombinedRefreshAction();
    }, uiState.refreshInterval);
  } else {
    if (refreshIntervalHandle) {
      clearInterval(refreshIntervalHandle);
      refreshIntervalHandle = null;
    }
  }
};

uiStatePart.select((s) => s).subscribe(() => startAutoRefresh());
loginStatePart.select((s) => s).subscribe(() => startAutoRefresh());
startAutoRefresh();

// ============================================================================
// TypedSocket — real-time server push (logs, events)
// ============================================================================

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

const upsertService = (
  services: interfaces.data.IService[],
  service: interfaces.data.IService,
): interfaces.data.IService[] => {
  const existingIndex = services.findIndex((item) => item.name === service.name);
  if (existingIndex === -1) {
    return [...services, service];
  }

  const updatedServices = [...services];
  updatedServices[existingIndex] = service;
  return updatedServices;
};

const upsertUpgradeOperation = (
  operations: interfaces.requests.IAppStoreUpgradeOperation[],
  operation: interfaces.requests.IAppStoreUpgradeOperation,
): interfaces.requests.IAppStoreUpgradeOperation[] => {
  const existingIndex = operations.findIndex((item) => item.id === operation.id);
  const updatedOperations = existingIndex === -1
    ? [operation, ...operations]
    : operations.map((item) => item.id === operation.id ? operation : item);
  return updatedOperations
    .sort((a, b) => b.startedAt - a.startedAt)
    .slice(0, 25);
};

socketRouter.addTypedHandler(
  new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushServiceUpdate>(
    'pushServiceUpdate',
    async (dataArg) => {
      const state = servicesStatePart.getState();
      let services = state.services;
      let currentService = state.currentService;
      let currentHostedAppLifecycle = state.currentHostedAppLifecycle;
      let currentServiceLogs = state.currentServiceLogs;
      let currentServiceStats = state.currentServiceStats;

      if (dataArg.action === 'deleted') {
        services = services.filter((service) => service.name !== dataArg.serviceName);
        if (currentService?.name === dataArg.serviceName) {
          currentService = null;
          currentHostedAppLifecycle = null;
          currentServiceLogs = [];
          currentServiceStats = null;
        }
      } else if (dataArg.service) {
        services = upsertService(services, dataArg.service);
        if (currentService?.name === dataArg.service.name) {
          currentService = dataArg.service;
        }
      }

      servicesStatePart.setState({
        ...state,
        services,
        currentService,
        currentHostedAppLifecycle,
        currentServiceLogs,
        currentServiceStats,
      });
      return {};
    },
  ),
);

socketRouter.addTypedHandler(
  new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushAppStoreUpgradeProgress>(
    'pushAppStoreUpgradeProgress',
    async (dataArg) => {
      const state = appStoreStatePart.getState();
      appStoreStatePart.setState({
        ...state,
        upgradeOperations: upsertUpgradeOperation(state.upgradeOperations, dataArg.operation),
        upgradeableServices: dataArg.operation.status === 'success'
          ? state.upgradeableServices.filter((service) => service.serviceName !== dataArg.operation.serviceName)
          : state.upgradeableServices,
      });
      if (dataArg.operation.service) {
        const servicesState = servicesStatePart.getState();
        servicesStatePart.setState({
          ...servicesState,
          services: upsertService(servicesState.services, dataArg.operation.service),
          currentService: servicesState.currentService?.name === dataArg.operation.service.name
            ? dataArg.operation.service
            : servicesState.currentService,
        });
      }
      return {};
    },
  ),
);

// Handle server-pushed platform service log entries
socketRouter.addTypedHandler(
  new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushPlatformServiceLog>(
    'pushPlatformServiceLog',
    async (dataArg) => {
      const state = servicesStatePart.getState();
      const entry: interfaces.data.ILogEntry = {
        id: state.currentPlatformServiceLogs.length,
        serviceId: 0,
        timestamp: new Date(dataArg.entry.timestamp).getTime(),
        message: dataArg.entry.message,
        level: dataArg.entry.level as 'info' | 'warn' | 'error' | 'debug',
        source: 'stdout',
      };
      const updated = [...state.currentPlatformServiceLogs, entry];
      // Cap at 2000 entries
      if (updated.length > 2000) {
        updated.splice(0, updated.length - 2000);
      }
      servicesStatePart.setState({
        ...state,
        currentPlatformServiceLogs: updated,
      });
      return {};
    },
  ),
);

// Handle server-pushed user service log entries
socketRouter.addTypedHandler(
  new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushServiceLog>(
    'pushServiceLog',
    async (dataArg) => {
      const state = servicesStatePart.getState();
      // Only append if we're currently viewing this service
      if (!state.currentService || state.currentService.name !== dataArg.serviceName) {
        return {};
      }
      const entry: interfaces.data.ILogEntry = {
        id: state.currentServiceLogs.length,
        serviceId: 0,
        timestamp: new Date(dataArg.entry.timestamp).getTime(),
        message: dataArg.entry.message,
        level: dataArg.entry.level as 'info' | 'warn' | 'error' | 'debug',
        source: 'stdout',
      };
      const updated = [...state.currentServiceLogs, entry];
      if (updated.length > 2000) {
        updated.splice(0, updated.length - 2000);
      }
      servicesStatePart.setState({
        ...state,
        currentServiceLogs: updated,
      });
      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');
    console.log('TypedSocket dashboard connection established');
  } catch (err) {
    console.error('TypedSocket connection failed:', err);
    socketClient = null;
  }
}

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

// ============================================================================
// App Store Actions
// ============================================================================

export const fetchAppStoreTemplatesAction = appStoreStatePart.createAction(
  async (statePartArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetAppStoreTemplates
      >('/typedrequest', 'getAppStoreTemplates');
      const response = await typedRequest.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), apps: response.apps };
    } catch (err) {
      console.error('Failed to fetch app templates:', err);
      return statePartArg.getState();
    }
  },
);

export const fetchUpgradeableAppStoreServicesAction = appStoreStatePart.createAction(
  async (statePartArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetUpgradeableAppStoreServices
      >('/typedrequest', 'getUpgradeableAppStoreServices');
      const response = await typedRequest.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), upgradeableServices: response.services };
    } catch (err) {
      console.error('Failed to fetch upgradeable services:', err);
      return statePartArg.getState();
    }
  },
);

export const fetchAppStoreUpgradeOperationsAction = appStoreStatePart.createAction(
  async (statePartArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetAppStoreUpgradeOperations
      >('/typedrequest', 'getAppStoreUpgradeOperations');
      const response = await typedRequest.fire({ identity: context.identity! });
      return { ...statePartArg.getState(), upgradeOperations: response.operations };
    } catch (err) {
      console.error('Failed to fetch upgrade operations:', err);
      return statePartArg.getState();
    }
  },
);

export const upgradeAppStoreServiceAction = appStoreStatePart.createAction<{
  serviceName: string;
  targetVersion: string;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_StartAppStoreServiceUpgrade
    >('/typedrequest', 'startAppStoreServiceUpgrade');
    const response = await typedRequest.fire({
      identity: context.identity!,
      serviceName: dataArg.serviceName,
      targetVersion: dataArg.targetVersion,
    });
    const state = statePartArg.getState();
    return {
      ...state,
      upgradeOperations: upsertUpgradeOperation(state.upgradeOperations, response.operation),
    };
  } catch (err) {
    console.error('Failed to upgrade service:', err);
    return statePartArg.getState();
  }
});

// Connect socket when logged in, disconnect when logged out
loginStatePart.select((s) => s).subscribe((loginState) => {
  if (loginState.isLoggedIn) {
    connectSocket();
  } else {
    disconnectSocket();
  }
});
