import * as tus from 'tus-js-client'

type FileRow = { name: string; path: string; is_dir: boolean; size: number; mtime: number }
type RowLike = {
  name?: unknown
  Name?: unknown
  path?: unknown
  Path?: unknown
  is_dir?: unknown
  IsDir?: unknown
  isDir?: unknown
  size?: unknown
  Size?: unknown
  mtime?: unknown
  Mtime?: unknown
}

type NoResumeStorage = {
  addUpload: (_u: any) => Promise<void>
  removeUpload: (_u: any) => Promise<void>
  listUploads: () => Promise<any[]>
  findUploadsByFingerprint: (_fp: string) => Promise<any[]>
}

const videoExt = new Set(['mp4', 'mkv', 'mov', 'avi', 'm4v', 'webm', 'mpg', 'mpeg', 'ts', 'm2ts'])
const imageExt = new Set(['jpg', 'jpeg', 'png', 'gif', 'heic', 'heif', 'webp', 'bmp', 'tif', 'tiff'])

const extLower = (n: string) => (n.includes('.') ? n.split('.').pop()!.toLowerCase() : '')

function sanitizeDesc(s: string) {
  s = s.trim().replace(/\s+/g, '_').replace(/[^A-Za-z0-9._-]+/g, '_')
  if (!s) s = 'upload'
  return s.slice(0, 64)
}

function sanitizeFolderName(s: string) {
  // Only allow a single path segment and collapse punctuation to keep mkdir/rename predictable.
  s = s.trim().replace(/[\/]+/g, '/').replace(/^\//, '').replace(/\/.*$/, '')
  s = s.replace(/\s+/g, '_').replace(/[^\w.\-]/g, '_').replace(/_+/g, '_')
  return s.slice(0, 64)
}

function extOf(n: string) {
  const i = n.lastIndexOf('.')
  return i > -1 ? n.slice(i + 1).toLowerCase() : ''
}

function stemOf(n: string) {
  const i = n.lastIndexOf('.')
  const stem = i > -1 ? n.slice(0, i) : n
  // Keep the stem safe and avoid extra dots inside the composed upload name.
  return sanitizeDesc(stem.replace(/\./g, '_')) || 'file'
}

function composeName(date: string, desc: string, orig: string) {
  const d = date || new Date().toISOString().slice(0, 10)
  const [Y, M, D] = d.split('-')
  const sDesc = sanitizeDesc(desc)
  const sStem = stemOf(orig)
  const ext = extOf(orig) || 'bin'
  return `${Y}.${M}.${D}.${sDesc}.${sStem}.${ext}`
}

function clampOneLevel(p: string) {
  if (!p) return ''
  return p.replace(/^\/+|\/+$/g, '').split('/')[0] || ''
}

function normalizeRows(listRaw: unknown[]): FileRow[] {
  return (Array.isArray(listRaw) ? listRaw : []).map((r: unknown) => {
    const row = (r && typeof r === 'object' ? r : {}) as RowLike
    return {
      name: String(row.name ?? row.Name ?? ''),
      path: String(row.path ?? row.Path ?? ''),
      is_dir: Boolean(row.is_dir ?? row.IsDir ?? row.isDir ?? false),
      size: Number(row.size ?? row.Size ?? 0),
      mtime: Number(row.mtime ?? row.Mtime ?? 0),
    }
  })
}

const isVideoFile = (f: File) => f.type.startsWith('video/') || videoExt.has(extLower(f.name))
const isImageFile = (f: File) => f.type.startsWith('image/') || imageExt.has(extLower(f.name))

function isDetailedError(e: unknown): e is tus.DetailedError {
  return typeof e === 'object' && e !== null && ('originalRequest' in (e as any) || 'originalResponse' in (e as any))
}

function isLikelyMobileUA(): boolean {
  if (typeof window === 'undefined') return false
  const ua = navigator.userAgent || ''
  const coarse = window.matchMedia && window.matchMedia('(pointer: coarse)').matches
  return coarse || /Mobi|Android|iPhone|iPad|iPod/i.test(ua)
}

function fmt(n: number) {
  if (n < 1024) return `${n} B`
  const units = ['KB', 'MB', 'GB', 'TB']
  let idx = -1
  do {
    n /= 1024
    idx++
  } while (n >= 1024 && idx < units.length - 1)
  return `${n.toFixed(1)} ${units[idx]}`
}

function createNoResumeStorage(): NoResumeStorage {
  return {
    async addUpload(_u: any) {},
    async removeUpload(_u: any) {},
    async listUploads() {
      return []
    },
    async findUploadsByFingerprint(_fp: string) {
      return []
    },
  }
}

async function createNoResumeFingerprint() {
  return `noresume-${Date.now()}-${Math.random().toString(36).slice(2)}`
}

const uploaderUtils = {
  sanitizeDesc,
  sanitizeFolderName,
  extOf,
  stemOf,
  composeName,
  clampOneLevel,
  normalizeRows,
  isVideoFile,
  isImageFile,
  isDetailedError,
  isLikelyMobileUA,
  fmt,
  createNoResumeStorage,
  createNoResumeFingerprint,
}

export default uploaderUtils
