import { createHash } from 'node:crypto'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import path from 'node:path'

import { ASSET_DIR, MEDIA_DIR } from './paths.js'

const MEDIA_URL_PREFIX = '/api/storage/media'

const MIME_EXTENSIONS: Record<string, 'jpg' | 'png'> = {
  'image/jpeg': 'jpg',
  'image/png': 'png'
}

const EXTENSION_CONTENT_TYPES: Record<string, string> = {
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.png': 'image/png'
}

type MediaFile = {
  bytes: Buffer
  contentType: string
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}

function saveDataUrl(dataUrl: string) {
  const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/)

  if (!match) {
    return dataUrl
  }

  const declaredContentType = match[1]
  const bytes = Buffer.from(match[2], 'base64')
  const sniffedExtension =
    bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff
      ? 'jpg'
      : bytes[0] === 0x89 &&
          bytes[1] === 0x50 &&
          bytes[2] === 0x4e &&
          bytes[3] === 0x47
        ? 'png'
        : undefined
  const extension = MIME_EXTENSIONS[declaredContentType] ?? sniffedExtension

  if (!extension) {
    return dataUrl
  }

  const hash = createHash('sha256').update(bytes).digest('hex')
  const fileName = `${hash}.${extension}`
  const filePath = path.join(ASSET_DIR, fileName)

  mkdirSync(ASSET_DIR, { recursive: true })

  if (!existsSync(filePath)) {
    writeFileSync(filePath, bytes)
  }

  return `${MEDIA_URL_PREFIX}/assets/${fileName}`
}

export function externalizeMedia(value: unknown): unknown {
  if (typeof value === 'string') {
    return value.startsWith('data:') ? saveDataUrl(value) : value
  }

  if (Array.isArray(value)) {
    return value.map((item) => externalizeMedia(item))
  }

  if (!isPlainObject(value)) {
    return value
  }

  const result: Record<string, unknown> = {}

  for (const [key, nestedValue] of Object.entries(value)) {
    if (key === 'blob' || key === 'jpg') {
      continue
    }

    result[key] = externalizeMedia(nestedValue)
  }

  return result
}

export function readMediaFile(segments: string[]): MediaFile | null {
  if (
    segments.length === 0 ||
    segments.some((segment) => !segment || segment === '.' || segment === '..')
  ) {
    return null
  }

  const requestedPath = path.join(MEDIA_DIR, ...segments)
  const relativePath = path.relative(MEDIA_DIR, requestedPath)

  if (relativePath.startsWith('..') || path.isAbsolute(relativePath) || !existsSync(requestedPath)) {
    return null
  }

  const contentType = EXTENSION_CONTENT_TYPES[path.extname(requestedPath).toLowerCase()]

  if (!contentType) {
    return null
  }

  return {
    bytes: readFileSync(requestedPath),
    contentType
  }
}
