import {
  CLOUD_SYNC_PAYLOAD_ENTITY_KEYS,
  type CloudSyncPayloadEntityKey,
  type SyncPayload,
} from './sync';

export type ShrinkFinding =
  | { suspicious: false }
  | {
      suspicious: true;
      reason: 'bulk-shrink' | 'large-shrink';
      entityType:
        | 'hosts'
        | 'keys'
        | 'identities'
        | 'proxyProfiles'
        | 'snippets'
        | 'customGroups'
        | 'snippetPackages'
        | 'portForwardingRules'
        | 'groupConfigs';
      baseCount: number;
      outgoingCount: number;
      lost: number;
      /** True when the comparison reference was the current remote (base was null). */
      viaRemote?: boolean;
    };

const CHECKED_ENTITIES = CLOUD_SYNC_PAYLOAD_ENTITY_KEYS;

type CheckedEntityType = CloudSyncPayloadEntityKey;

const BULK_SHRINK_RATIO = 0.5;
const BULK_SHRINK_MIN_ABSOLUTE = 3;
const LARGE_SHRINK_ABSOLUTE = 10;

function countOf(p: SyncPayload, key: CheckedEntityType): number {
  const v = p[key];
  return Array.isArray(v) ? v.length : 0;
}

export function detectSuspiciousShrink(
  outgoing: SyncPayload,
  base: SyncPayload | null,
  remote?: SyncPayload | null,
): ShrinkFinding {
  // Fall back to the current remote when we have no stored base — a null base
  // happens on first sync, after unlock key re-derivation, or when the base
  // blob failed to decrypt. Without this fallback, a degraded/empty local
  // payload would be admitted unconditionally and could overwrite populated
  // remote data (#779). We only use `remote` when `base` is unavailable so
  // legitimate resurrections (device that legitimately grew past an older
  // remote snapshot) remain unaffected.
  const reference = base ?? remote ?? null;
  const viaRemote = !base && !!remote;
  if (!reference) return { suspicious: false };

  for (const entityType of CHECKED_ENTITIES) {
    const baseCount = countOf(reference, entityType);
    const outgoingCount = countOf(outgoing, entityType);
    const lost = baseCount - outgoingCount;
    if (lost <= 0) continue;

    if (lost >= LARGE_SHRINK_ABSOLUTE) {
      return {
        suspicious: true,
        reason: 'large-shrink',
        entityType,
        baseCount,
        outgoingCount,
        lost,
        ...(viaRemote ? { viaRemote: true } : {}),
      };
    }

    if (baseCount > 0 && lost / baseCount >= BULK_SHRINK_RATIO && lost >= BULK_SHRINK_MIN_ABSOLUTE) {
      return {
        suspicious: true,
        reason: 'bulk-shrink',
        entityType,
        baseCount,
        outgoingCount,
        lost,
        ...(viaRemote ? { viaRemote: true } : {}),
      };
    }
  }

  return { suspicious: false };
}
