import {
  requireStudyAuth,
  normalizeStudySlug,
  normalizeLessonSectionId,
  getStudySlugFromNoteId,
  isStudyUserEnrolled,
  isEnrollableStudySlug,
  getStudyTitleBySlug,
  findStudyUserById,
  getStudyAvatarUrl,
  getStudyCatalog,
} from '../study-helpers.js'
import { state } from '../state.js'
import {
  queueStudyUsersWrite,
  queueStudyCommunityWrite,
  loadUserNotes,
  queueUserNotesWrite,
  loadUserProgress,
  queueUserProgressWrite,
  sanitizeStudyCommunityPosts,
} from '../data.js'
import { MAX_STUDY_NOTE_LENGTH, MAX_STUDY_NOTES_PER_USER, MAX_STUDY_ENROLLMENTS_PER_USER } from '../config.js'
import { randomUUID } from 'node:crypto'

const noteSaveHits = new Map() // userId -> { count, windowStart }
const NOTE_RATE_WINDOW_MS = 60 * 1000
const NOTE_RATE_MAX = 30

function checkNoteSaveRateLimit(userId) {
  const now = Date.now()
  const entry = noteSaveHits.get(userId) ?? { count: 0, windowStart: now }
  if (now - entry.windowStart > NOTE_RATE_WINDOW_MS) { entry.count = 0; entry.windowStart = now }
  entry.count += 1
  noteSaveHits.set(userId, entry)
  return entry.count <= NOTE_RATE_MAX
}

export function register(app) {
  // ── Enrollment ────────────────────────────────────────────────────────────

  app.get('/api/study-enrollment', requireStudyAuth, (req, res) => {
    const user = req.studyUser
    res.json({
      enrolledStudySlugs: Array.isArray(user.enrolledStudySlugs) ? user.enrolledStudySlugs : [],
      availableStudies: getStudyCatalog()
        .map(study => ({ slug: study.slug, title: study.title })),
    })
  })

  app.post('/api/study-enrollment/:studySlug', requireStudyAuth, (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    if (!studySlug || !isEnrollableStudySlug(studySlug)) {
      res.status(404).json({ message: 'Study not found.' })
      return
    }

    const enrolled = Array.isArray(user.enrolledStudySlugs) ? user.enrolledStudySlugs : []
    if (!enrolled.includes(studySlug)) {
      user.enrolledStudySlugs = [...enrolled, studySlug].slice(0, MAX_STUDY_ENROLLMENTS_PER_USER)
      user.updatedAt = new Date().toISOString()
      queueStudyUsersWrite()
    }

    res.json({
      ok: true,
      studySlug,
      studyTitle: getStudyTitleBySlug(studySlug) || studySlug,
      enrolledStudySlugs: user.enrolledStudySlugs,
    })
  })

  app.delete('/api/study-enrollment/:studySlug', requireStudyAuth, (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    if (!studySlug || !isEnrollableStudySlug(studySlug)) {
      res.status(404).json({ message: 'Study not found.' })
      return
    }

    const enrolled = Array.isArray(user.enrolledStudySlugs) ? user.enrolledStudySlugs : []
    if (enrolled.includes(studySlug)) {
      user.enrolledStudySlugs = enrolled.filter(slug => slug !== studySlug)
      user.updatedAt = new Date().toISOString()
      queueStudyUsersWrite()
    }

    res.json({
      ok: true,
      studySlug,
      studyTitle: getStudyTitleBySlug(studySlug) || studySlug,
      enrolledStudySlugs: user.enrolledStudySlugs,
    })
  })

  // ── Notes ─────────────────────────────────────────────────────────────────

  app.get('/api/study-notes', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const notes = await loadUserNotes(user.id)
    res.json({ notes })
  })

  app.get('/api/study-notes/:sectionId', requireStudyAuth, async (req, res) => {
    const sectionId = normalizeLessonSectionId(req.params.sectionId)
    if (!sectionId) {
      res.status(400).json({ message: 'Invalid section id.' })
      return
    }
    const user = req.studyUser
    const noteStudySlug = getStudySlugFromNoteId(sectionId)
    if (noteStudySlug && !isStudyUserEnrolled(user, noteStudySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to access notes.' })
      return
    }
    const notes = await loadUserNotes(user.id)
    res.json({ note: notes[sectionId] ?? '' })
  })

  app.put('/api/study-notes/:sectionId', requireStudyAuth, async (req, res) => {
    const sectionId = normalizeLessonSectionId(req.params.sectionId)
    if (!sectionId) {
      res.status(400).json({ message: 'Invalid section id.' })
      return
    }
    const user = req.studyUser
    if (!checkNoteSaveRateLimit(user.id)) {
      res.status(429).json({ message: 'Too many note saves. Please slow down.' })
      return
    }
    const noteStudySlug = getStudySlugFromNoteId(sectionId)
    if (noteStudySlug && !isStudyUserEnrolled(user, noteStudySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to save notes.' })
      return
    }
    const rawNote = typeof req.body?.note === 'string' ? req.body.note : ''
    const note = rawNote.trim().slice(0, MAX_STUDY_NOTE_LENGTH)
    const notes = await loadUserNotes(user.id)

    if (!note) {
      delete notes[sectionId]
    } else {
      const existingCount = Object.keys(notes).length
      if (!notes[sectionId] && existingCount >= MAX_STUDY_NOTES_PER_USER) {
        res.status(400).json({ message: 'Notes limit reached for this account.' })
        return
      }
      notes[sectionId] = note
    }

    state.studyNotesCache.set(user.id, notes)
    queueUserNotesWrite(user.id)
    res.json({ ok: true, note })
  })

  // ── Progress ──────────────────────────────────────────────────────────────

  app.get('/api/study-progress/:studySlug', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    if (!studySlug) {
      res.status(400).json({ message: 'Study slug is required.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to view progress.' })
      return
    }

    const progress = await loadUserProgress(user.id)
    const completedSectionIds = progress.byStudy[studySlug]?.completedSectionIds ?? []

    // Record first visit timestamp on the user record (additive, never overwrite)
    if (!user.firstVisitAt) {
      user.firstVisitAt = new Date().toISOString()
      queueStudyUsersWrite()
    }

    res.json({ studySlug, completedSectionIds })
  })

  app.post('/api/study-progress/:studySlug/:sectionId', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    const sectionId = normalizeLessonSectionId(req.params.sectionId)
    if (!studySlug || !sectionId) {
      res.status(400).json({ message: 'Invalid study slug or section id.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to update progress.' })
      return
    }

    const progress = await loadUserProgress(user.id)
    const studyProgress = progress.byStudy[studySlug] ?? { completedSectionIds: [] }
    if (!studyProgress.completedSectionIds.includes(sectionId)) {
      studyProgress.completedSectionIds = [...studyProgress.completedSectionIds, sectionId]
    }
    progress.byStudy[studySlug] = studyProgress
    progress.updatedAt = new Date().toISOString()
    state.studyProgressCache.set(user.id, progress)
    queueUserProgressWrite(user.id)

    // Streak tracking
    const todayStr = new Date().toISOString().slice(0, 10)
    const lastDate = user.lastStudiedDate ?? null
    if (lastDate !== todayStr) {
      const yesterday = new Date(Date.now() - 864e5).toISOString().slice(0, 10)
      user.currentStreak = lastDate === yesterday ? (user.currentStreak ?? 0) + 1 : 1
      user.longestStreak = Math.max(user.currentStreak, user.longestStreak ?? 0)
      user.lastStudiedDate = todayStr
    }

    // Record first completion timestamp on the user record (additive, never overwrite)
    if (!user.firstCompletionAt) {
      user.firstCompletionAt = new Date().toISOString()
    }
    queueStudyUsersWrite()

    res.json({ ok: true, studySlug, completedSectionIds: studyProgress.completedSectionIds })
  })

  app.delete('/api/study-progress/:studySlug/:sectionId', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    const sectionId = normalizeLessonSectionId(req.params.sectionId)
    if (!studySlug || !sectionId) {
      res.status(400).json({ message: 'Invalid study slug or section id.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to update progress.' })
      return
    }

    const progress = await loadUserProgress(user.id)
    const studyProgress = progress.byStudy[studySlug] ?? { completedSectionIds: [] }
    studyProgress.completedSectionIds = studyProgress.completedSectionIds.filter(id => id !== sectionId)
    progress.byStudy[studySlug] = studyProgress
    progress.updatedAt = new Date().toISOString()
    state.studyProgressCache.set(user.id, progress)
    queueUserProgressWrite(user.id)

    res.json({ ok: true, studySlug, completedSectionIds: studyProgress.completedSectionIds })
  })

  // ── Quiz ──────────────────────────────────────────────────────────────────

  app.get('/api/study-quiz/:studySlug/:sectionId', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    const sectionId = normalizeLessonSectionId(req.params.sectionId)
    if (!studySlug || !sectionId) {
      res.status(400).json({ message: 'Invalid study slug or section id.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to view quiz answers.' })
      return
    }

    const progress = await loadUserProgress(user.id)
    const quizAnswers = progress.byStudy[studySlug]?.quizAnswers?.[sectionId] ?? []
    res.json({ studySlug, sectionId, answers: quizAnswers })
  })

  app.post('/api/study-quiz/:studySlug/:sectionId', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.params.studySlug)
    const sectionId = normalizeLessonSectionId(req.params.sectionId)
    if (!studySlug || !sectionId) {
      res.status(400).json({ message: 'Invalid study slug or section id.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to save quiz answers.' })
      return
    }

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

    const progress = await loadUserProgress(user.id)
    const studyProgress = progress.byStudy[studySlug] ?? { completedSectionIds: [] }
    studyProgress.quizAnswers = studyProgress.quizAnswers || {}
    studyProgress.quizAnswers[sectionId] = answers
    progress.byStudy[studySlug] = studyProgress
    progress.updatedAt = new Date().toISOString()
    state.studyProgressCache.set(user.id, progress)
    queueUserProgressWrite(user.id)

    res.json({ ok: true, studySlug, sectionId, answers })
  })

  // ── Community ─────────────────────────────────────────────────────────────

  app.get('/api/study-community', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(typeof req.query?.studySlug === 'string' ? req.query.studySlug : '')

    if (!studySlug) {
      res.status(400).json({ message: 'Study slug is required.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to view the community.' })
      return
    }

    const posts = state.studyCommunityPosts
      .filter(post => post.studySlug === studySlug)
      .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
      .slice(0, 50)
      .map(post => {
        const author = findStudyUserById(post.authorUserId)
        return {
          ...post,
          authorAvatarUrl: getStudyAvatarUrl(author || post.authorName || ''),
          replies: Array.isArray(post.replies)
            ? post.replies.map(reply => {
              const replyAuthor = findStudyUserById(reply.authorUserId)
              return { ...reply, authorAvatarUrl: getStudyAvatarUrl(replyAuthor || reply.authorName || '') }
            })
            : [],
        }
      })

    res.json({ studySlug, posts })
  })

  app.post('/api/study-community/posts', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const studySlug = normalizeStudySlug(req.body?.studySlug)
    const sectionId = typeof req.body?.sectionId === 'string' && /^[a-z0-9-]{1,80}$/i.test(req.body.sectionId) ? req.body.sectionId.trim() : ''
    const message = typeof req.body?.message === 'string' ? req.body.message.trim().slice(0, 3000) : ''

    if (!studySlug || !message) {
      res.status(400).json({ message: 'Study slug and message are required.' })
      return
    }

    if (!isStudyUserEnrolled(user, studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to post in the community.' })
      return
    }

    const now = new Date().toISOString()
    const authorName = user.displayName?.trim() || (user.username?.includes('@') ? user.username.split('@')[0] : user.username)
    const post = {
      id: randomUUID(),
      studySlug,
      sectionId,
      authorUserId: user.id,
      authorName,
      authorAvatarUrl: getStudyAvatarUrl(user.username),
      message,
      createdAt: now,
      replies: [],
    }

    state.studyCommunityPosts.unshift(post)
    state.studyCommunityPosts = sanitizeStudyCommunityPosts(state.studyCommunityPosts).slice(0, 500)
    queueStudyCommunityWrite()

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

  app.post('/api/study-community/posts/:postId/replies', requireStudyAuth, async (req, res) => {
    const user = req.studyUser
    const postId = typeof req.params.postId === 'string' ? req.params.postId.trim() : ''
    const message = typeof req.body?.message === 'string' ? req.body.message.trim().slice(0, 3000) : ''

    if (!postId || !message) {
      res.status(400).json({ message: 'Post id and message are required.' })
      return
    }

    const post = state.studyCommunityPosts.find(item => item.id === postId)
    if (!post) {
      res.status(404).json({ message: 'Post not found.' })
      return
    }

    if (!isStudyUserEnrolled(user, post.studySlug)) {
      res.status(403).json({ message: 'Please enroll in this study to reply in the community.' })
      return
    }

    const reply = {
      id: randomUUID(),
      authorUserId: user.id,
      authorName: user.displayName?.trim() || (user.username?.includes('@') ? user.username.split('@')[0] : user.username),
      authorAvatarUrl: getStudyAvatarUrl(user.username),
      message,
      createdAt: new Date().toISOString(),
    }

    post.replies = Array.isArray(post.replies) ? post.replies : []
    post.replies.push(reply)
    post.replies = sanitizeStudyCommunityPosts([post])[0]?.replies ?? []
    queueStudyCommunityWrite()

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