import { ActionType, OpType, Operation, SyncImportReason } from '../core/operation.types';
import {
  SyncProviderBase,
  OperationSyncCapable,
  OperationSyncProviderMode,
  SyncOperation,
} from '../sync-providers/provider.interface';
import { SyncProviderId } from '../sync-providers/provider.const';

/** Provider IDs that use file-based operation sync (WebDAV, Dropbox, OneDrive, LocalFile, Nextcloud) */
const FILE_BASED_PROVIDER_IDS: Set<SyncProviderId> = new Set([
  SyncProviderId.WebDAV,
  SyncProviderId.Dropbox,
  SyncProviderId.OneDrive,
  SyncProviderId.LocalFile,
  SyncProviderId.Nextcloud,
]);

const OPERATION_SYNC_PROVIDER_MODES: Set<OperationSyncProviderMode> = new Set([
  'superSyncOps',
  'fileSnapshotOps',
]);

/**
 * Type guard to check if a provider supports operation-based sync (API sync).
 * This is for providers like SuperSync that have a dedicated API endpoint.
 */
export const isOperationSyncCapable = (
  provider: SyncProviderBase<SyncProviderId>,
): provider is SyncProviderBase<SyncProviderId> & OperationSyncCapable => {
  const providerMode = (provider as { providerMode?: OperationSyncProviderMode })
    .providerMode;
  return (
    'supportsOperationSync' in provider &&
    (provider as unknown as OperationSyncCapable).supportsOperationSync === true &&
    providerMode !== undefined &&
    OPERATION_SYNC_PROVIDER_MODES.has(providerMode)
  );
};

/**
 * Type guard to check if a provider uses file-based operation sync.
 * File-based providers (WebDAV, Dropbox, OneDrive, LocalFile, Nextcloud) use file storage for sync.
 */
export const isFileBasedProvider = (
  provider: SyncProviderBase<SyncProviderId>,
): boolean => {
  return isFileBasedProviderId(provider.id);
};

/**
 * Id-based sibling of `isFileBasedProvider` for callers that only have the
 * provider id, not a resolved provider instance (e.g. the sync-config dialog
 * deciding whether to offer pre-upload encryption during first-time setup).
 */
export const isFileBasedProviderId = (id: SyncProviderId): boolean => {
  return FILE_BASED_PROVIDER_IDS.has(id);
};

const VALID_OP_TYPES = new Set<string>(Object.values(OpType));

/**
 * Convert a SyncOperation (from API response) to an Operation (local format).
 */
export const syncOpToOperation = (syncOp: SyncOperation): Operation => {
  if (!VALID_OP_TYPES.has(syncOp.opType)) {
    throw new Error(`Invalid opType from server: '${syncOp.opType}'`);
  }

  return {
    id: syncOp.id,
    clientId: syncOp.clientId,
    actionType: syncOp.actionType as ActionType,
    opType: syncOp.opType as Operation['opType'],
    entityType: syncOp.entityType as Operation['entityType'],
    entityId: syncOp.entityId,
    entityIds: syncOp.entityIds,
    payload: syncOp.payload,
    vectorClock: syncOp.vectorClock,
    timestamp: syncOp.timestamp,
    schemaVersion: syncOp.schemaVersion,
    ...(syncOp.syncImportReason
      ? { syncImportReason: syncOp.syncImportReason as SyncImportReason }
      : {}),
  };
};
