import { eq } from 'drizzle-orm'
import bcrypt from 'bcryptjs'
import { users } from '../db/schema/users.js'
import { RbacService } from '../services/rbac.service.js'
import type { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'

/**
 * Permission inheritance — same logic as auth.ts
 */
const ACTION_HIERARCHY: Record<string, string[]> = {
  admin: ['admin', 'edit', 'view'],
  edit: ['edit', 'view'],
  view: ['view'],
  upload: ['upload'],
  delete: ['delete'],
  send: ['send'],
  export: ['export'],
}

function expandPermissions(slugs: string[]): Set<string> {
  const expanded = new Set<string>()
  for (const slug of slugs) {
    expanded.add(slug)
    const [domain, action] = slug.split('.')
    const implied = ACTION_HIERARCHY[action]
    if (implied && domain) {
      for (const a of implied) expanded.add(`${domain}.${a}`)
    }
  }
  return expanded
}

// Simple rate limiter: track failed attempts per IP
const failedAttempts = new Map<string, { count: number; resetAt: number }>()
const MAX_FAILED_ATTEMPTS = 10
const LOCKOUT_DURATION = 5 * 60 * 1000 // 5 minutes

function checkRateLimit(ip: string): boolean {
  const now = Date.now()
  const entry = failedAttempts.get(ip)
  if (!entry || entry.resetAt < now) return true // allowed
  return entry.count < MAX_FAILED_ATTEMPTS
}

function recordFailure(ip: string) {
  const now = Date.now()
  const entry = failedAttempts.get(ip)
  if (!entry || entry.resetAt < now) {
    failedAttempts.set(ip, { count: 1, resetAt: now + LOCKOUT_DURATION })
  } else {
    entry.count++
  }
}

function clearFailures(ip: string) {
  failedAttempts.delete(ip)
}

// Periodic cleanup of old entries
setInterval(() => {
  const now = Date.now()
  for (const [ip, entry] of failedAttempts) {
    if (entry.resetAt < now) failedAttempts.delete(ip)
  }
}, 60_000)

/**
 * WebDAV Basic Auth pre-handler.
 * Verifies HTTP Basic Auth credentials against the users table
 * and attaches request.user / request.permissions just like JWT auth.
 */
export function webdavBasicAuth(app: FastifyInstance) {
  return async function (request: FastifyRequest, reply: FastifyReply) {
    const ip = request.ip

    if (!checkRateLimit(ip)) {
      reply.header('Retry-After', '300')
      return reply.status(429).send('Too many failed attempts. Try again later.')
    }

    const authHeader = request.headers.authorization
    if (!authHeader || !authHeader.startsWith('Basic ')) {
      reply.header('WWW-Authenticate', 'Basic realm="LunarFront WebDAV"')
      return reply.status(401).send('Authentication required')
    }

    const decoded = Buffer.from(authHeader.slice(6), 'base64').toString('utf-8')
    const colonIndex = decoded.indexOf(':')
    if (colonIndex === -1) {
      recordFailure(ip)
      reply.header('WWW-Authenticate', 'Basic realm="LunarFront WebDAV"')
      return reply.status(401).send('Invalid credentials')
    }

    const email = decoded.slice(0, colonIndex)
    const password = decoded.slice(colonIndex + 1)

    const [user] = await app.db
      .select({ id: users.id, passwordHash: users.passwordHash, isActive: users.isActive, role: users.role })
      .from(users)
      .where(eq(users.email, email.toLowerCase()))
      .limit(1)

    if (!user || !user.isActive) {
      recordFailure(ip)
      reply.header('WWW-Authenticate', 'Basic realm="LunarFront WebDAV"')
      return reply.status(401).send('Invalid credentials')
    }

    const valid = await bcrypt.compare(password, user.passwordHash)
    if (!valid) {
      recordFailure(ip)
      reply.header('WWW-Authenticate', 'Basic realm="LunarFront WebDAV"')
      return reply.status(401).send('Invalid credentials')
    }

    // Success — clear failure counter
    clearFailures(ip)
    request.user = { id: user.id, role: user.role }
    const permSlugs = await RbacService.getUserPermissions(app.db, user.id)
    request.permissions = expandPermissions(permSlugs)
  }
}
