import { File as ExpoFile } from 'expo-file-system'
import type { Visibility } from 'src/lib/api-types'
import { AccountStore } from 'src/state/accountStore'
import { appStorage } from 'src/state/storage'
import type { MediaAsset } from './types'

/**
 * Per-account MMKV key prefix. Mastodon Pass 3 Phase 1 §2.4 / §9.2 — the
 * draft is the only piece of compose-time state that used to be device-
 * global, so a draft started on Account A would surface in Account B's
 * composer (caption, attached media, quoted status id — see the parent
 * plan's §1.10 for the failure modes). The prefix lets account-teardown
 * code (accountStore.ts → `clearDraftFor`, `clearAllDrafts`) walk every
 * active draft without round-tripping through this module's saver/loader.
 *
 * The legacy device-global key `ui.composer.draft.v1` is wiped on first
 * launch by `bootCleanupV1` so installs that pre-date the per-account
 * scoping start clean.
 */
const DRAFT_KEY_PREFIX = 'ui.composer.draft.v1.'

function activeDraftKey(): string {
  return `${DRAFT_KEY_PREFIX}${AccountStore.activeIdOrThrow()}`
}

export type DraftMedia = Pick<
  MediaAsset,
  'uri' | 'kind' | 'mimeType' | 'altText' | 'width' | 'height' | 'durationMs'
>

export type ComposerDraft = {
  caption: string
  scope: Visibility
  sensitive: boolean
  /** Compose Redesign Phase 3: optional content-warning text. Persisted so a
   * resumed draft restores the CW alongside the sensitive flag. Empty string
   * when unset. Pre-Phase-3 drafts will lack this key; readers default to ''. */
  spoilerText: string
  media: DraftMedia[]
  quotedStatusId: string | null
  savedAt: number
}

/** Reads the active account's stored draft and drops media that no longer
 *  exists on disk (iOS aggressively purges the picker cache between
 *  sessions). */
export function loadDraft(): ComposerDraft | null {
  const raw = appStorage.getString(activeDraftKey())
  if (!raw) return null
  try {
    const parsed = JSON.parse(raw) as ComposerDraft
    const surviving = parsed.media.filter((m) => {
      try {
        return new ExpoFile(m.uri).exists
      } catch {
        return false
      }
    })
    if (surviving.length !== parsed.media.length) {
      return { ...parsed, media: surviving }
    }
    return parsed
  } catch {
    return null
  }
}

export function saveDraft(draft: Omit<ComposerDraft, 'savedAt'>): void {
  const hasContent =
    draft.caption.trim().length > 0 ||
    draft.media.length > 0 ||
    !!draft.quotedStatusId ||
    draft.spoilerText.trim().length > 0
  if (!hasContent) {
    clearDraft()
    return
  }
  const payload: ComposerDraft = { ...draft, savedAt: Date.now() }
  try {
    appStorage.set(activeDraftKey(), JSON.stringify(payload))
  } catch {
    /* quota or serialization — drop silently, drafts are best-effort */
  }
}

export function clearDraft(): void {
  appStorage.remove(activeDraftKey())
}

/**
 * Drop the saved draft for a specific account. Currently has no
 * registry-side caller — the soft-delete (`AccountStore.markSignedOut`)
 * deliberately preserves drafts so a re-sign-in lands warm, and
 * `clearAll()` uses `clearAllDrafts` to nuke every entry at once. Kept as
 * a public helper for future surgical drops (e.g. an admin-mode reset).
 */
export function clearDraftFor(accountId: string): void {
  appStorage.remove(`${DRAFT_KEY_PREFIX}${accountId}`)
}

/**
 * Drop every per-account draft on the device. Used by AccountStore
 * .clearAll (Sign out of all accounts). Walks `appStorage.getAllKeys()`
 * because the registry has already been wiped by the time this runs and
 * accountIds are no longer enumerable from the registry side.
 */
export function clearAllDrafts(): void {
  for (const k of appStorage.getAllKeys()) {
    if (k.startsWith(DRAFT_KEY_PREFIX)) appStorage.remove(k)
  }
}

/**
 * Sum the on-disk size of every media file referenced by every saved draft
 * across all accounts. Returns 0 when no drafts exist or when every
 * referenced URI has been purged from the picker cache by the OS. Used by
 * the Settings → Storage screen to surface the drafts footprint honestly
 * without conflating it with the cache categories — drafts are user data
 * and never get bulk-cleared.
 *
 * The MMKV JSON itself is negligible (a few hundred bytes per draft); the
 * disk weight is in the picker-cache files the URIs point at.
 */
export function getAllDraftsBytes(): number {
  let total = 0
  for (const k of appStorage.getAllKeys()) {
    if (!k.startsWith(DRAFT_KEY_PREFIX)) continue
    const raw = appStorage.getString(k)
    if (!raw) continue
    try {
      const parsed = JSON.parse(raw) as ComposerDraft
      for (const m of parsed.media) {
        try {
          const f = new ExpoFile(m.uri)
          if (f.exists) total += f.size
        } catch {
          /* missing file — drop silently */
        }
      }
    } catch {
      /* malformed draft — skip */
    }
  }
  return total
}

export function toDraftMedia(media: MediaAsset[]): DraftMedia[] {
  return media.map((m) => ({
    uri: m.uri,
    kind: m.kind,
    mimeType: m.mimeType,
    altText: m.altText,
    width: m.width,
    height: m.height,
    durationMs: m.durationMs,
  }))
}
