import { mkdir, stat, unlink, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { inferImageExtensionFromDataUrl, normalizeAssetBaseName } from '../helpers.js'
import { requireAdminAuth } from '../auth.js'
import { UPLOADS_DIR } from '../config.js'
import { state } from '../state.js'
import {
  listUploadedAssets,
  readUploadsMetadata,
  writeUploadsMetadata,
  getUserNotesFilePath,
  queueStudyUsersWrite,
} from '../data.js'
import {
  hashStudyPassword,
  getStudyCatalog,
  normalizeStudySlug,
  isEnrollableStudySlug,
} from '../study-helpers.js'

export function register(app) {
  app.get('/api/admin-assets', requireAdminAuth, async (_req, res) => {
    try {
      const assets = await listUploadedAssets()
      res.json({ assets })
    } catch {
      res.status(500).json({ message: 'Could not list uploaded assets.' })
    }
  })

  app.post('/api/admin-assets', requireAdminAuth, async (req, res) => {
    try {
      const filename = typeof req.body?.filename === 'string' ? req.body.filename : ''
      const dataUrl = typeof req.body?.dataUrl === 'string' ? req.body.dataUrl : ''
      const ext = inferImageExtensionFromDataUrl(dataUrl)

      if (!ext) {
        res.status(400).json({ message: 'Upload must be a PNG, JPG, WEBP, GIF, PDF, DOC, or DOCX data URL.' })
        return
      }

      const base64 = dataUrl.split(',')[1] ?? ''
      const buffer = Buffer.from(base64, 'base64')
      if (buffer.length === 0 || buffer.length > (8 * 1024 * 1024)) {
        res.status(400).json({ message: 'Upload must be between 1 byte and 8MB.' })
        return
      }

      const baseName = normalizeAssetBaseName(filename.replace(/\.[a-z0-9]+$/i, ''))
      const finalName = `${baseName}-${Date.now()}${ext}`

      await mkdir(UPLOADS_DIR, { recursive: true })
      await writeFile(path.join(UPLOADS_DIR, finalName), buffer)
      const metadata = await readUploadsMetadata()
      metadata[finalName] = []
      await writeUploadsMetadata(metadata)

      res.json({ ok: true, asset: { filename: finalName, url: `/uploads/${finalName}` } })
    } catch {
      res.status(500).json({ message: 'Upload failed.' })
    }
  })

  app.patch('/api/admin-assets/:filename', requireAdminAuth, async (req, res) => {
    try {
      const { filename } = req.params
      if (typeof filename !== 'string' || filename.includes('/') || filename.includes('..')) {
        res.status(400).json({ message: 'Invalid filename.' })
        return
      }

      const tags = Array.isArray(req.body?.tags)
        ? req.body.tags.filter(tag => typeof tag === 'string').map(tag => tag.trim()).filter(Boolean)
        : []

      const filePath = path.join(UPLOADS_DIR, filename)
      await stat(filePath)

      const metadata = await readUploadsMetadata()
      metadata[filename] = tags
      await writeUploadsMetadata(metadata)

      res.json({ ok: true, tags })
    } catch {
      res.status(404).json({ message: 'Asset not found.' })
    }
  })

  app.delete('/api/admin-assets/:filename', requireAdminAuth, async (req, res) => {
    try {
      const { filename } = req.params
      if (typeof filename !== 'string' || filename.includes('/') || filename.includes('..')) {
        res.status(400).json({ message: 'Invalid filename.' })
        return
      }

      await unlink(path.join(UPLOADS_DIR, filename))
      const metadata = await readUploadsMetadata()
      delete metadata[filename]
      await writeUploadsMetadata(metadata)
      res.json({ ok: true })
    } catch {
      res.status(404).json({ message: 'Asset not found.' })
    }
  })

  // ── Admin: Study User Management ─────────────────────────────────────────

  app.get('/api/admin/study-users', requireAdminAuth, async (req, res) => {
    const catalog = getStudyCatalog()
    const users = await Promise.all(state.studyUsers.map(async user => {
      let noteCount = 0
      try {
        const { readFile } = await import('node:fs/promises')
        const notesRaw = await readFile(getUserNotesFilePath(user.id), 'utf8').catch(() => '{}')
        const notes = JSON.parse(notesRaw)
        noteCount = Object.values(notes).filter(n => typeof n === 'string' && n.trim()).length
      } catch { /* ignore */ }

      const enrolledStudies = (user.enrolledStudySlugs ?? []).map(slug => {
        const study = catalog.find(s => s.slug === slug)
        return study ? { slug, title: study.title } : { slug, title: slug }
      })

      return {
        id: user.id,
        username: user.username,
        displayName: user.displayName ?? '',
        createdAt: user.createdAt ?? null,
        lastLoginAt: user.lastLoginAt ?? null,
        enrolledStudies,
        noteCount,
        subscribeNewsletter: user.subscribeNewsletter !== false,
        studyRemindersEnabled: user.studyRemindersEnabled === true,
      }
    }))

    res.json({ users })
  })

  app.patch('/api/admin/study-users/:id', requireAdminAuth, (req, res) => {
    const user = state.studyUsers.find(u => u.id === req.params.id)
    if (!user) { res.status(404).json({ message: 'User not found.' }); return }

    const { displayName, newPassword, addEnrollment, removeEnrollment } = req.body ?? {}

    if (typeof displayName === 'string') {
      user.displayName = displayName.trim().slice(0, 80)
    }

    if (typeof newPassword === 'string') {
      if (newPassword.length < 8 || newPassword.length > 200) {
        res.status(400).json({ message: 'Password must be 8–200 characters.' }); return
      }
      user.passwordHash = hashStudyPassword(newPassword)
      for (const [token, session] of state.studySessions) {
        if (session.userId === user.id) state.studySessions.delete(token)
      }
    }

    if (typeof addEnrollment === 'string' && addEnrollment.trim()) {
      const slug = normalizeStudySlug(addEnrollment)
      if (!slug || !isEnrollableStudySlug(slug)) {
        res.status(400).json({ message: `Unknown study slug: ${addEnrollment.trim()}` }); return
      }
      if (!Array.isArray(user.enrolledStudySlugs)) user.enrolledStudySlugs = []
      if (!user.enrolledStudySlugs.includes(slug)) user.enrolledStudySlugs.push(slug)
    }

    if (typeof removeEnrollment === 'string' && removeEnrollment.trim()) {
      const slug = removeEnrollment.trim()
      user.enrolledStudySlugs = (user.enrolledStudySlugs ?? []).filter(s => s !== slug)
    }

    user.updatedAt = new Date().toISOString()
    queueStudyUsersWrite()
    res.json({ ok: true, displayName: user.displayName, enrolledStudySlugs: user.enrolledStudySlugs })
  })

  app.delete('/api/admin/study-users/:id', requireAdminAuth, async (req, res) => {
    const user = state.studyUsers.find(u => u.id === req.params.id)
    if (!user) { res.status(404).json({ message: 'User not found.' }); return }

    for (const [token, session] of state.studySessions) {
      if (session.userId === user.id) state.studySessions.delete(token)
    }

    state.studyUsers = state.studyUsers.filter(u => u.id !== user.id)
    queueStudyUsersWrite()

    state.studyNotesCache.delete(user.id)
    try { await unlink(getUserNotesFilePath(user.id)) } catch { /* no notes file is fine */ }

    res.json({ ok: true })
  })
}
