import {blobStore} from './blob-store'
import {guardedFetch, readCapped} from './fetch-guard'
import {reencodeWebp} from './image'

const MAX_SOURCE_IMAGE_BYTES = 10 * 1024 * 1024
const DISPLAY_WIDTH = 1080

export {InvalidImageError, type ProcessedImage} from './image'

// the shared processor for any crumb-bound image (auto or manual): wide-cap
// webp that preserves aspect, strips exif, neutralizes crafted payloads
export const processCrumbImage = (input: Uint8Array) =>
  reencodeWebp(input, {
    width: DISPLAY_WIDTH,
    fit: 'inside',
    maxInputBytes: MAX_SOURCE_IMAGE_BYTES,
    withoutEnlargement: true,
  })

export const crumbImageKey = (crumbId: string, hash: string) =>
  `crumb-images/${crumbId}/${hash}.webp`

export const trailCoverKey = (trailId: string, hash: string) =>
  `trail-covers/${trailId}/${hash}.webp`

const displayImageKey = (hash: string) => `source-images/${hash}.webp`

const assetImageKey = (userId: string, hash: string) =>
  `screengrabs/${userId}/${hash}.webp`

// best-effort — never throws into the extract path
export const fetchRemoteImage = async (
  rawUrl: string,
): Promise<Uint8Array | null> => {
  try {
    const res = await guardedFetch(rawUrl, 'image/*')
    if (!res?.ok) return null
    const type = res.headers.get('content-type') ?? ''
    if (!type.startsWith('image/')) return null
    const bytes = await readCapped(res, MAX_SOURCE_IMAGE_BYTES)
    return bytes.byteLength ? bytes : null
  } catch {
    return null
  }
}

const store = async (
  bytes: Uint8Array,
  key: (hash: string) => string,
): Promise<string | null> => {
  try {
    const processed = await processCrumbImage(bytes)
    const ref = key(processed.hash)
    await blobStore.put(ref, processed.bytes, processed.contentType)
    return ref
  } catch {
    return null
  }
}

// fetch + store a source's display image (a link's og:image); null on failure
export const storeSourceDisplayImage = async (
  imageUrl: string,
): Promise<string | null> => {
  const bytes = await fetchRemoteImage(imageUrl)
  return bytes ? store(bytes, displayImageKey) : null
}

// store the original generation input for provenance (a screengrab)
export const storeSourceAsset = (
  userId: string,
  input: Uint8Array,
): Promise<string | null> =>
  store(input, (hash) => assetImageKey(userId, hash))

export const publicImageUrl = (ref: string | null): string | null =>
  ref ? blobStore.publicUrl(ref) : null

// the crumb's display image, resolved by precedence:
// manual crumb image > source display image > source asset
export const crumbImageUrl = (
  crumb: {imageRef: string | null},
  source: {imageRef: string | null; assetRef: string | null} | null,
): string | null =>
  publicImageUrl(
    crumb.imageRef ?? source?.imageRef ?? source?.assetRef ?? null,
  )
