import 'server-only'
import { cookies } from 'next/headers'
import { SignJWT, jwtVerify } from 'jose'
import { scryptSync, timingSafeEqual, randomBytes } from 'node:crypto'
import { db } from './db'

/**
 * Two audiences, two session kinds. A student session can never reach the parent
 * routes: scores, grader justifications and generation rationales are written
 * for Eric and would land badly on a ten-year-old.
 */

const COOKIE = 'learn_session'
const ALG = 'HS256'

function secret(): Uint8Array {
  const s = process.env.SESSION_SECRET
  if (!s) throw new Error('SESSION_SECRET is not set')
  return new TextEncoder().encode(s)
}

export type Session =
  | { kind: 'parent'; id: string; name: string }
  | { kind: 'student'; id: string; name: string; slug: string }

export function hashPasscode(plain: string): string {
  const salt = randomBytes(16)
  const hash = scryptSync(plain, salt, 64)
  return `${salt.toString('hex')}:${hash.toString('hex')}`
}

export function verifyPasscode(plain: string, stored: string): boolean {
  const [saltHex, hashHex] = stored.split(':')
  if (!saltHex || !hashHex) return false
  const hash = Buffer.from(hashHex, 'hex')
  const candidate = scryptSync(plain, Buffer.from(saltHex, 'hex'), hash.length)
  return hash.length === candidate.length && timingSafeEqual(hash, candidate)
}

export async function createSession(session: Session): Promise<void> {
  const token = await new SignJWT(session as unknown as Record<string, unknown>)
    .setProtectedHeader({ alg: ALG })
    .setIssuedAt()
    .setExpirationTime('30d')
    .sign(secret())

  const jar = await cookies()
  jar.set(COOKIE, token, {
    httpOnly: true,
    sameSite: 'lax',
    secure: process.env.SECURE_COOKIES === 'true',
    path: '/',
    maxAge: 60 * 60 * 24 * 30,
  })
}

export async function getSession(): Promise<Session | null> {
  const jar = await cookies()
  const token = jar.get(COOKIE)?.value
  if (!token) return null
  try {
    const { payload } = await jwtVerify(token, secret())
    return payload as unknown as Session
  } catch {
    return null
  }
}

export async function destroySession(): Promise<void> {
  const jar = await cookies()
  jar.delete(COOKIE)
}

export async function requireParent(): Promise<Extract<Session, { kind: 'parent' }>> {
  const s = await getSession()
  if (!s || s.kind !== 'parent') throw new Error('UNAUTHORIZED')
  return s
}

export async function requireStudent(): Promise<Extract<Session, { kind: 'student' }>> {
  const s = await getSession()
  if (!s || s.kind !== 'student') throw new Error('UNAUTHORIZED')
  return s
}

export async function authenticateParent(email: string, password: string) {
  const parent = await db.parent.findUnique({ where: { email: email.toLowerCase().trim() } })
  if (!parent || !verifyPasscode(password, parent.password)) return null
  return parent
}

export async function authenticateStudent(slug: string, passcode: string) {
  const student = await db.student.findUnique({ where: { slug } })
  if (!student || !verifyPasscode(passcode, student.passcode)) return null
  return student
}
