import {
  classifyForUpload,
  COMPRESS_REASONABLE_RATIO,
  hasBlockingItem,
  needsConsent,
  RESIZE_REASONABLE_RATIO,
} from 'src/lib/media/classify'
import type { MediaLimits } from 'src/lib/media/limits'
import type { RawAsset } from 'src/lib/media/types'

const MB = 1024 * 1024

const baseLimits: MediaLimits = {
  maxImageBytes: 5 * MB,
  maxVideoBytes: 50 * MB,
  maxAttachments: 4,
  maxAltTextLength: 1000,
  maxCharacters: 500,
  defaultScope: 'public',
  quotePolicy: 'public',
  charactersReservedPerUrl: null,
  mediaRequired: true,
  maxSpoilerLength: 140,
  maxHashtagLength: 124,
}

function photo(overrides: Partial<RawAsset> = {}): RawAsset {
  return {
    uri: 'file:///tmp/pic.jpg',
    kind: 'photo',
    mimeType: 'image/jpeg',
    ...overrides,
  }
}

function video(overrides: Partial<RawAsset> = {}): RawAsset {
  return {
    uri: 'file:///tmp/clip.mp4',
    kind: 'video',
    mimeType: 'video/mp4',
    ...overrides,
  }
}

describe('classifyForUpload — photos', () => {
  test('photo within limit is within_limits', () => {
    const c = classifyForUpload(photo({ size: 2 * MB }), baseLimits)
    expect(c.tag).toBe('within_limits')
  })

  test('HEIC within limit is will_transcode_only', () => {
    const c = classifyForUpload(
      photo({ mimeType: 'image/heic', size: 2 * MB }),
      baseLimits
    )
    expect(c.tag).toBe('will_transcode_only')
  })

  test('12 MB JPEG against 5 MB limit is will_resize with target', () => {
    const c = classifyForUpload(photo({ size: 12 * MB }), baseLimits)
    expect(c.tag).toBe('will_resize')
    if (c.tag === 'will_resize') {
      expect(c.targetBytes).toBe(5 * MB)
      expect(c.sizeBytes).toBe(12 * MB)
    }
  })

  test('80 MB JPEG against 5 MB limit is too_big_to_resize (16x over)', () => {
    const c = classifyForUpload(photo({ size: 80 * MB }), baseLimits)
    expect(c.tag).toBe('too_big_to_resize')
    if (c.tag === 'too_big_to_resize') {
      expect(c.ratio).toBeCloseTo(16, 0)
      expect(c.isAnimatedGif).toBe(false)
    }
  })

  test('oversized animated GIF flags isAnimatedGif', () => {
    const c = classifyForUpload(
      photo({ mimeType: 'image/gif', size: 12 * MB }),
      baseLimits
    )
    expect(c.tag).toBe('too_big_to_resize')
    if (c.tag === 'too_big_to_resize') {
      expect(c.isAnimatedGif).toBe(true)
    }
  })

  test('photo with unknown size is within_limits (no false positive)', () => {
    const c = classifyForUpload(photo({ size: undefined }), baseLimits)
    expect(c.tag).toBe('within_limits')
  })

  test('HEIC with unknown size is will_transcode_only', () => {
    const c = classifyForUpload(photo({ mimeType: 'image/heic' }), baseLimits)
    expect(c.tag).toBe('will_transcode_only')
  })

  test('HEIC, server allowlist excludes both jpeg/webp → unsupported_mime', () => {
    const c = classifyForUpload(
      photo({ mimeType: 'image/heic', size: 2 * MB }),
      baseLimits,
      { supportedImageMimeTypes: ['image/png'] }
    )
    expect(c.tag).toBe('unsupported_mime')
  })

  test('photo just over limit but under reasonable ratio → will_resize', () => {
    // RESIZE_REASONABLE_RATIO = 6, so 5x the limit is still resize-able.
    const c = classifyForUpload(
      photo({ size: 5 * baseLimits.maxImageBytes }),
      baseLimits
    )
    expect(c.tag).toBe('will_resize')
  })

  test('photo just past reasonable ratio → too_big_to_resize', () => {
    const c = classifyForUpload(
      photo({ size: (RESIZE_REASONABLE_RATIO + 0.5) * baseLimits.maxImageBytes }),
      baseLimits
    )
    expect(c.tag).toBe('too_big_to_resize')
  })
})

describe('classifyForUpload — videos', () => {
  test('60 MB MP4 against 50 MB limit is will_compress', () => {
    const c = classifyForUpload(video({ size: 60 * MB }), baseLimits)
    expect(c.tag).toBe('will_compress')
    if (c.tag === 'will_compress') {
      expect(c.targetBytes).toBe(50 * MB)
    }
  })

  test('220 MB MP4 against 50 MB limit is too_big_to_compress with ratio', () => {
    const c = classifyForUpload(video({ size: 220 * MB }), baseLimits)
    expect(c.tag).toBe('too_big_to_compress')
    if (c.tag === 'too_big_to_compress') {
      expect(c.ratio).toBeCloseTo(4.4, 1)
    }
  })

  test('non-MP4 video within limit is will_transcode_only', () => {
    const c = classifyForUpload(
      video({ mimeType: 'video/quicktime', size: 20 * MB }),
      baseLimits
    )
    expect(c.tag).toBe('will_transcode_only')
  })

  test('mp4 within limit is within_limits', () => {
    const c = classifyForUpload(video({ size: 20 * MB }), baseLimits)
    expect(c.tag).toBe('within_limits')
  })

  test('video with unknown size is within_limits (no false positive)', () => {
    const c = classifyForUpload(video({ size: undefined }), baseLimits)
    expect(c.tag).toBe('within_limits')
  })

  test('non-MP4 with unknown size is will_transcode_only', () => {
    const c = classifyForUpload(
      video({ mimeType: 'video/quicktime', size: undefined }),
      baseLimits
    )
    expect(c.tag).toBe('will_transcode_only')
  })

  test('exactly at the compress reasonable ratio → will_compress', () => {
    const c = classifyForUpload(
      video({ size: COMPRESS_REASONABLE_RATIO * baseLimits.maxVideoBytes }),
      baseLimits
    )
    expect(c.tag).toBe('will_compress')
  })
})

describe('needsConsent / hasBlockingItem', () => {
  test('all-within batches do not need consent', () => {
    const items = [photo({ size: 1 * MB }), video({ size: 10 * MB })].map((r) =>
      classifyForUpload(r, baseLimits)
    )
    expect(needsConsent(items)).toBe(false)
    expect(hasBlockingItem(items)).toBe(false)
  })

  test('transcode-only batches do not need consent', () => {
    const items = [
      photo({ mimeType: 'image/heic', size: 2 * MB }),
      video({ mimeType: 'video/quicktime', size: 10 * MB }),
    ].map((r) => classifyForUpload(r, baseLimits))
    expect(needsConsent(items)).toBe(false)
    expect(hasBlockingItem(items)).toBe(false)
  })

  test('a will_resize item triggers consent but does not block', () => {
    const items = [classifyForUpload(photo({ size: 12 * MB }), baseLimits)]
    expect(needsConsent(items)).toBe(true)
    expect(hasBlockingItem(items)).toBe(false)
  })

  test('a too_big_to_compress item blocks', () => {
    const items = [classifyForUpload(video({ size: 220 * MB }), baseLimits)]
    expect(needsConsent(items)).toBe(true)
    expect(hasBlockingItem(items)).toBe(true)
  })

  test('mixed batch: 1 ok, 1 resize, 1 too-big', () => {
    const items = [
      photo({ size: 1 * MB }),
      photo({ size: 12 * MB }),
      video({ size: 220 * MB }),
    ].map((r) => classifyForUpload(r, baseLimits))
    expect(needsConsent(items)).toBe(true)
    expect(hasBlockingItem(items)).toBe(true)
  })
})
