import { Image } from 'expo-image'
import {
  clearVideoCacheAsync,
  getCurrentVideoCacheSize,
  setVideoCacheSizeAsync,
} from 'expo-video'
import { Platform } from 'react-native'
import { appStorage } from 'src/state/storage'

// P3b/P3c — central place where the media-cache caps and the cache-eviction
// policy live. Boot calls `applyVideoCacheCap()` and `applyImageCacheCap()`
// once at app launch so the platform-side limits match the user's stored
// preference (or our defaults). The Settings → Storage screen calls the
// `setVideoCacheCap` / `setImageCacheCap` setters when the user picks a new
// limit; subsequent boots pick the new value back up.

const VIDEO_CACHE_KEY = 'media.videoCacheBytes'
const IMAGE_CACHE_KEY = 'media.imageCacheBytes'
const POLICY_KEY = 'media.autoEvictPolicy'
const LAST_EVICT_KEY = 'media.lastEvictionAt'

// 256 MB. Pixelfed's encoded MP4s are typically 4–20 MB, so this holds
// 12–60 recently-watched videos — enough for a healthy scroll-back window
// without quietly accumulating into the gigabytes the user notices in iOS
// Settings → Storage weeks later. The OS can clear the cache under storage
// pressure regardless, so this is an upper bound, not a hard guarantee.
export const DEFAULT_VIDEO_CACHE_BYTES = 256 * 1024 * 1024

// 512 MB on iOS only — Android (Coil) has no programmatic configureCache API
// and uses its own internal LRU. expo-image's iOS default is 0 (unlimited);
// without this cap the disk cache can drift into the gigabyte range over
// scroll-heavy weeks.
export const DEFAULT_IMAGE_CACHE_BYTES = 512 * 1024 * 1024

export const VIDEO_CACHE_PRESETS = [
  { label: '128 MB', bytes: 128 * 1024 * 1024 },
  { label: '256 MB', bytes: 256 * 1024 * 1024 },
  { label: '512 MB', bytes: 512 * 1024 * 1024 },
  { label: '1 GB', bytes: 1024 * 1024 * 1024 },
  { label: '2 GB', bytes: 2 * 1024 * 1024 * 1024 },
  { label: 'Off', bytes: 0 },
] as const

export const IMAGE_CACHE_PRESETS = [
  { label: '128 MB', bytes: 128 * 1024 * 1024 },
  { label: '256 MB', bytes: 256 * 1024 * 1024 },
  { label: '512 MB', bytes: 512 * 1024 * 1024 },
  { label: '1 GB', bytes: 1024 * 1024 * 1024 },
  { label: '2 GB', bytes: 2 * 1024 * 1024 * 1024 },
] as const

export type AutoEvictPolicy = 'never' | 'weekly' | 'monthly'

const POLICY_INTERVALS_MS: Record<AutoEvictPolicy, number> = {
  never: Number.POSITIVE_INFINITY,
  weekly: 7 * 24 * 60 * 60 * 1000,
  monthly: 30 * 24 * 60 * 60 * 1000,
}

export function getVideoCacheCap(): number {
  return appStorage.getNumber(VIDEO_CACHE_KEY) ?? DEFAULT_VIDEO_CACHE_BYTES
}

export function getImageCacheCap(): number {
  return appStorage.getNumber(IMAGE_CACHE_KEY) ?? DEFAULT_IMAGE_CACHE_BYTES
}

export function getAutoEvictPolicy(): AutoEvictPolicy {
  const stored = appStorage.getString(POLICY_KEY)
  if (stored === 'weekly' || stored === 'monthly' || stored === 'never') return stored
  return 'never'
}

export function setAutoEvictPolicy(policy: AutoEvictPolicy): void {
  appStorage.set(POLICY_KEY, policy)
}

export async function applyVideoCacheCap(): Promise<void> {
  try {
    await setVideoCacheSizeAsync(getVideoCacheCap())
  } catch {
    // expo-video throws on platforms where the native module is not yet
    // ready; the cap is best-effort and a missed apply is harmless (the
    // built-in default still bounds growth).
  }
}

export async function setVideoCacheCap(bytes: number): Promise<void> {
  appStorage.set(VIDEO_CACHE_KEY, bytes)
  try {
    await setVideoCacheSizeAsync(bytes)
  } catch {}
}

export function applyImageCacheCap(): void {
  // expo-image's `configureCache` only honours `maxDiskSize` on iOS; on
  // Android the call is a no-op and we rely on Coil's internal LRU. Skip
  // the call entirely on Android so we don't pay the bridge round-trip.
  if (Platform.OS !== 'ios') return
  try {
    Image.configureCache({ maxDiskSize: getImageCacheCap() })
  } catch {}
}

export function setImageCacheCap(bytes: number): void {
  appStorage.set(IMAGE_CACHE_KEY, bytes)
  if (Platform.OS !== 'ios') return
  try {
    Image.configureCache({ maxDiskSize: bytes })
  } catch {}
}

export function getCurrentVideoCacheBytes(): number {
  try {
    return getCurrentVideoCacheSize()
  } catch {
    return 0
  }
}

// P3e — periodic auto-wipe. Called fire-and-forget at boot. Exits early if
// the policy is 'never' or the configured interval has not yet elapsed since
// the last fire. Drafts are explicitly NOT touched here — they're user data,
// not cache (see `lib/media/draft.ts`).
export async function maybeAutoEvict(): Promise<void> {
  const policy = getAutoEvictPolicy()
  const interval = POLICY_INTERVALS_MS[policy]
  if (!Number.isFinite(interval)) return
  const last = appStorage.getNumber(LAST_EVICT_KEY) ?? 0
  if (Date.now() - last < interval) return
  await Promise.all([
    Image.clearDiskCache().catch(() => false),
    Image.clearMemoryCache().catch(() => false),
    clearVideoCacheAsync().catch(() => undefined),
  ])
  appStorage.set(LAST_EVICT_KEY, Date.now())
}

export async function clearAllMediaCaches(): Promise<void> {
  await Promise.all([
    Image.clearDiskCache().catch(() => false),
    Image.clearMemoryCache().catch(() => false),
    clearVideoCacheAsync().catch(() => undefined),
  ])
  // Re-apply the caps so platforms that reset on clear pick the user's
  // chosen limit back up immediately.
  await applyVideoCacheCap()
  applyImageCacheCap()
  appStorage.set(LAST_EVICT_KEY, Date.now())
}

export async function clearImageCache(): Promise<void> {
  await Promise.all([
    Image.clearDiskCache().catch(() => false),
    Image.clearMemoryCache().catch(() => false),
  ])
  applyImageCacheCap()
}

export async function clearVideoCache(): Promise<void> {
  try {
    await clearVideoCacheAsync()
  } catch {}
  await applyVideoCacheCap()
}
