/**
 * DegradationService — Phase 4: file degradation over time.
 *
 * Photos accumulate and bloat the app. This service progressively compresses
 * ("degrades") old full-resolution photos so they take far less space while the
 * JSON (the source of truth) survives intact. Users are notified before/after.
 *
 * Strategy (safe, reversible-ish, never deletes the thumbnails or the JSON):
 *   - Photos older than 180 days in 'full' state  -> compress to COMPRESSED
 *   - Photos older than 365 days already compressed -> optionally drop to thumbnail-only
 *
 * The user is always notified of how much space was reclaimed. Nothing is
 * permanently deleted without explicit user action — we only compress, and the
 * JSON memory + thumbnails always remain.
 */
import * as FileSystem from 'expo-file-system';
import type { PhotoRecord } from './types';
import { loadPhotoRecords, savePhotoRecord, compressOriginalPhoto } from './photoStorageService';

declare const __DEV__: boolean;

function isDevRuntime() {
  return typeof __DEV__ !== 'undefined' && __DEV__;
}
function logDev(message: string, details: Record<string, unknown>) {
  if (isDevRuntime()) console.log(message, details);
}

export type DegradationResult = {
  compressedCount: number;
  reclaimedBytes: number;
  skippedCount: number;
};

const DAY_MS = 24 * 60 * 60 * 1000;

function ageDays(iso: string): number {
  const t = new Date(iso).getTime();
  if (!t) return 0;
  return Math.max(0, (Date.now() - t) / DAY_MS);
}

async function fileBytes(uri?: string): Promise<number> {
  if (!uri) return 0;
  try {
    const info = await FileSystem.getInfoAsync(uri);
    return info.exists && 'size' in info ? (info.size || 0) : 0;
  } catch {
    return 0;
  }
}

/**
 * Run degradation once. Only touches photos in 'full' state older than
 * `compressOlderThanDays` (default 180). Returns what was reclaimed so the
 * caller can notify the user. Safe to run on every app start — idempotent.
 */
export async function runPhotoDegradation(compressOlderThanDays = 180): Promise<DegradationResult> {
  const records = await loadPhotoRecords();
  let compressedCount = 0;
  let reclaimedBytes = 0;
  let skippedCount = 0;

  for (const record of Object.values(records)) {
    if (record.storageState !== 'full') {
      skippedCount += 1;
      continue;
    }
    if (ageDays(record.createdAt) < compressOlderThanDays) {
      skippedCount += 1;
      continue;
    }

    const beforeBytes = await fileBytes(record.originalUri);
    const updated = await compressOriginalPhoto(record.photoId);
    if (updated) {
      const afterBytes = await fileBytes(updated.compressedUri);
      reclaimedBytes += Math.max(0, beforeBytes - afterBytes);
      compressedCount += 1;
    } else {
      skippedCount += 1;
    }
  }

  logDev('[Degradation] run complete', { compressedCount, reclaimedBytes, skippedCount });
  return { compressedCount, reclaimedBytes, skippedCount };
}

/**
 * Count photos that WOULD be degraded (for a "what will happen" preview or
 * a proactive notification). Does not modify anything.
 */
export async function previewDegradation(compressOlderThanDays = 180): Promise<DegradationResult> {
  const records = await loadPhotoRecords();
  let compressedCount = 0;
  let reclaimedBytes = 0;
  let skippedCount = 0;

  for (const record of Object.values(records)) {
    if (record.storageState !== 'full' || ageDays(record.createdAt) < compressOlderThanDays) {
      skippedCount += 1;
      continue;
    }
    const size = await fileBytes(record.originalUri);
    // Compressed ≈ ~15-20% of original by default; estimate conservatively.
    reclaimedBytes += Math.round(size * 0.8);
    compressedCount += 1;
  }

  return { compressedCount, reclaimedBytes, skippedCount };
}

/** Human-readable bytes. */
export function formatBytes(bytes: number): string {
  if (bytes >= 1_000_000_000) return `${(bytes / 1_000_000_000).toFixed(1)} GB`;
  if (bytes >= 1_000_000) return `${(bytes / 1_000_000).toFixed(1)} MB`;
  if (bytes >= 1_000) return `${(bytes / 1_000).toFixed(1)} KB`;
  return `${bytes} B`;
}
