import * as plugins from '../plugins.js';
import * as interfaces from '../../ts_interfaces/index.js';
import { appState } from './shared.js';
import { getActionContext } from './login.js';

// ============================================================================
// 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;
  targetProfileIds?: string[];
  description?: string;

  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,
      targetProfileIds: dataArg.targetProfileIds,
      description: dataArg.description,

      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;
  targetProfileIds?: string[];

  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,
      targetProfileIds: dataArg.targetProfileIds,

      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 };
  },
);

// ============================================================================
