const { describe, test, expect, beforeEach, afterEach, mock } = require('bun:test')

describe('Admin Panel Functionality', () => {
  let adminSessions
  let adminLoginAttempts
  
  const ADMIN_COOKIE_NAME = 'admin_session'
  const ADMIN_SESSION_TTL_MS = 24 * 60 * 60 * 1000
  const ADMIN_LOGIN_RATE_LIMIT = 5

  beforeEach(() => {
    adminSessions = new Map()
    adminLoginAttempts = new Map()
  })

  afterEach(() => {
    adminSessions.clear()
    adminLoginAttempts.clear()
  })

  describe('Admin Session Management', () => {
    function readAdminSessionToken(req) {
      try {
        const cookieHeader = req?.headers?.cookie || ''
        if (!cookieHeader) return ''
        const parts = cookieHeader.split(';').map((p) => p.trim())
        const match = parts.find((p) => p.startsWith(`${ADMIN_COOKIE_NAME}=`))
        if (!match) return ''
        return decodeURIComponent(match.split('=').slice(1).join('=') || '')
      } catch (_) {
        return ''
      }
    }

    function getAdminSession(req) {
      const token = readAdminSessionToken(req)
      if (!token) return null
      const session = adminSessions.get(token)
      if (!session) return null
      if (session.expiresAt && session.expiresAt < Date.now()) {
        adminSessions.delete(token)
        return null
      }
      return { token, expiresAt: session.expiresAt }
    }

    function startAdminSession() {
      const crypto = require('crypto')
      const token = crypto.randomUUID()
      const expiresAt = Date.now() + ADMIN_SESSION_TTL_MS
      adminSessions.set(token, { expiresAt })
      return token
    }

    function clearAdminSession(token) {
      if (token) adminSessions.delete(token)
    }

    function requireAdminAuth(req) {
      const session = getAdminSession(req)
      if (!session) return null
      return session
    }

    test('starts a new admin session', () => {
      const token = startAdminSession()
      
      expect(token).toBeDefined()
      expect(adminSessions.has(token)).toBe(true)
      expect(adminSessions.get(token).expiresAt).toBeGreaterThan(Date.now())
    })

    test('reads admin session token from cookie', () => {
      const req = {
        headers: {
          cookie: `${ADMIN_COOKIE_NAME}=test-token-123; other=value`
        }
      }
      
      const token = readAdminSessionToken(req)
      expect(token).toBe('test-token-123')
    })

    test('returns null for missing cookie', () => {
      const req = { headers: {} }
      const token = readAdminSessionToken(req)
      expect(token).toBe('')
    })

    test('validates active session', () => {
      const token = startAdminSession()
      const req = {
        headers: { cookie: `${ADMIN_COOKIE_NAME}=${token}` }
      }
      
      const session = getAdminSession(req)
      expect(session).not.toBeNull()
      expect(session.token).toBe(token)
    })

    test('rejects expired session', () => {
      const crypto = require('crypto')
      const token = crypto.randomUUID()
      adminSessions.set(token, { expiresAt: Date.now() - 1000 })
      
      const req = {
        headers: { cookie: `${ADMIN_COOKIE_NAME}=${token}` }
      }
      
      const session = getAdminSession(req)
      expect(session).toBeNull()
      expect(adminSessions.has(token)).toBe(false)
    })

    test('rejects invalid token', () => {
      const req = {
        headers: { cookie: `${ADMIN_COOKIE_NAME}=invalid-token` }
      }
      
      const session = getAdminSession(req)
      expect(session).toBeNull()
    })

    test('clears admin session', () => {
      const token = startAdminSession()
      clearAdminSession(token)
      
      expect(adminSessions.has(token)).toBe(false)
    })

    test('requireAdminAuth returns session for valid auth', () => {
      const token = startAdminSession()
      const req = {
        headers: { cookie: `${ADMIN_COOKIE_NAME}=${token}` }
      }
      
      const session = requireAdminAuth(req)
      expect(session).not.toBeNull()
    })

    test('requireAdminAuth returns null for invalid auth', () => {
      const req = { headers: {} }
      const session = requireAdminAuth(req)
      expect(session).toBeNull()
    })
  })

  describe('Admin Login Rate Limiting', () => {
    const LOGIN_LOCKOUT_MS = 900000
    const RATE_LIMIT_WINDOW_MS = 60000

    function checkLoginRateLimit(ip, limit, attempts) {
      const now = Date.now()
      const record = attempts.get(ip)
      
      if (!record) {
        attempts.set(ip, { count: 1, windowStart: now, lockedUntil: null })
        return { blocked: false, count: 1 }
      }
      
      if (record.lockedUntil && record.lockedUntil > now) {
        return { 
          blocked: true, 
          retryAfter: Math.ceil((record.lockedUntil - now) / 1000)
        }
      }
      
      if (now - record.windowStart > RATE_LIMIT_WINDOW_MS) {
        attempts.set(ip, { count: 1, windowStart: now, lockedUntil: null })
        return { blocked: false, count: 1 }
      }
      
      record.count++
      
      if (record.count >= limit) {
        record.lockedUntil = now + LOGIN_LOCKOUT_MS
        return { blocked: true, retryAfter: Math.ceil(LOGIN_LOCKOUT_MS / 1000) }
      }
      
      return { blocked: false, count: record.count }
    }

    test('allows first login attempt', () => {
      const result = checkLoginRateLimit('192.168.1.1', ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      
      expect(result.blocked).toBe(false)
      expect(result.count).toBe(1)
    })

    test('counts attempts within window', () => {
      const ip = '192.168.1.1'
      checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      const result = checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      
      expect(result.blocked).toBe(false)
      expect(result.count).toBe(3)
    })

    test('blocks after exceeding limit', () => {
      const ip = '192.168.1.1'
      for (let i = 0; i < ADMIN_LOGIN_RATE_LIMIT; i++) {
        checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      }
      const result = checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      
      expect(result.blocked).toBe(true)
      expect(result.retryAfter).toBeGreaterThan(0)
    })

    test('resets after lockout period', () => {
      const ip = '192.168.1.1'
      for (let i = 0; i < ADMIN_LOGIN_RATE_LIMIT; i++) {
        checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      }
      
      const record = adminLoginAttempts.get(ip)
      record.lockedUntil = Date.now() - 1000
      
      const result = checkLoginRateLimit(ip, ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      expect(result.blocked).toBe(false)
    })

    test('tracks different IPs separately', () => {
      checkLoginRateLimit('192.168.1.1', ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      checkLoginRateLimit('192.168.1.1', ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      checkLoginRateLimit('192.168.1.2', ADMIN_LOGIN_RATE_LIMIT, adminLoginAttempts)
      
      expect(adminLoginAttempts.get('192.168.1.1').count).toBe(2)
      expect(adminLoginAttempts.get('192.168.1.2').count).toBe(1)
    })
  })

  describe('Admin User Management', () => {
    let usersDb

    beforeEach(() => {
      usersDb = [
        { id: 'user-1', email: 'user1@test.com', plan: 'hobby', billingStatus: 'active' },
        { id: 'user-2', email: 'user2@test.com', plan: 'starter', billingStatus: 'active' },
        { id: 'user-3', email: 'user3@test.com', plan: 'professional', billingStatus: 'active' }
      ]
    })

    function findUserById(userId) {
      return usersDb.find(u => u.id === userId) || null
    }

    function normalizePlanSelection(plan) {
      const validPlans = ['hobby', 'starter', 'professional', 'enterprise']
      return validPlans.includes(plan) ? plan : 'hobby'
    }

    function isPaidPlan(plan) {
      return ['starter', 'professional', 'enterprise'].includes(plan)
    }

    test('finds user by ID', () => {
      const user = findUserById('user-1')
      expect(user).toBeDefined()
      expect(user.email).toBe('user1@test.com')
    })

    test('returns null for non-existent user', () => {
      const user = findUserById('non-existent')
      expect(user).toBeNull()
    })

    test('normalizes valid plan selections', () => {
      expect(normalizePlanSelection('hobby')).toBe('hobby')
      expect(normalizePlanSelection('starter')).toBe('starter')
      expect(normalizePlanSelection('professional')).toBe('professional')
      expect(normalizePlanSelection('enterprise')).toBe('enterprise')
    })

    test('defaults invalid plan to hobby', () => {
      expect(normalizePlanSelection('invalid')).toBe('hobby')
      expect(normalizePlanSelection('')).toBe('hobby')
      expect(normalizePlanSelection(null)).toBe('hobby')
    })

    test('identifies paid plans', () => {
      expect(isPaidPlan('hobby')).toBe(false)
      expect(isPaidPlan('starter')).toBe(true)
      expect(isPaidPlan('professional')).toBe(true)
      expect(isPaidPlan('enterprise')).toBe(true)
    })

    test('updates user plan', () => {
      const user = findUserById('user-1')
      user.plan = 'enterprise'
      
      expect(user.plan).toBe('enterprise')
    })

    test('lists all users', () => {
      expect(usersDb.length).toBe(3)
    })

    test('deletes user', () => {
      const initialCount = usersDb.length
      const index = usersDb.findIndex(u => u.id === 'user-1')
      usersDb.splice(index, 1)
      
      expect(usersDb.length).toBe(initialCount - 1)
      expect(findUserById('user-1')).toBeNull()
    })
  })

  describe('Admin Token Management', () => {
    let tokenUsage
    let usersDb

    beforeEach(() => {
      tokenUsage = {}
      usersDb = [
        { id: 'user-1', plan: 'hobby' },
        { id: 'user-2', plan: 'professional' }
      ]
    })

    function ensureTokenUsageBucket(userId) {
      if (!tokenUsage[userId]) {
        tokenUsage[userId] = { usage: 0, tokenOverride: null }
      }
      return tokenUsage[userId]
    }

    function getPlanTokenLimits(plan, userId) {
      const limits = {
        hobby: 10000,
        starter: 50000,
        professional: 200000,
        enterprise: 1000000
      }
      const bucket = tokenUsage[userId]
      if (bucket?.tokenOverride !== null && bucket?.tokenOverride !== undefined) {
        return bucket.tokenOverride
      }
      return limits[plan] || limits.hobby
    }

    test('creates token bucket for new user', () => {
      const bucket = ensureTokenUsageBucket('new-user')
      
      expect(bucket).toBeDefined()
      expect(bucket.usage).toBe(0)
      expect(bucket.tokenOverride).toBeNull()
    })

    test('returns existing bucket', () => {
      tokenUsage['existing-user'] = { usage: 5000, tokenOverride: 20000 }
      const bucket = ensureTokenUsageBucket('existing-user')
      
      expect(bucket.usage).toBe(5000)
      expect(bucket.tokenOverride).toBe(20000)
    })

    test('sets token limit override', () => {
      const userId = 'user-1'
      const bucket = ensureTokenUsageBucket(userId)
      bucket.tokenOverride = 50000
      
      expect(bucket.tokenOverride).toBe(50000)
    })

    test('clears override when set to 0', () => {
      const userId = 'user-1'
      const bucket = ensureTokenUsageBucket(userId)
      bucket.tokenOverride = 50000
      bucket.tokenOverride = 0 ? null : bucket.tokenOverride
      
      bucket.tokenOverride = null
      
      expect(bucket.tokenOverride).toBeNull()
    })

    test('sets usage directly', () => {
      const userId = 'user-1'
      const bucket = ensureTokenUsageBucket(userId)
      bucket.usage = 25000
      
      expect(bucket.usage).toBe(25000)
    })

    test('calculates remaining tokens', () => {
      const userId = 'user-1'
      const bucket = ensureTokenUsageBucket(userId)
      const limit = getPlanTokenLimits('hobby', userId)
      bucket.usage = 5000
      const remaining = limit - bucket.usage
      
      expect(remaining).toBe(5000)
    })

    test('uses override limit when set', () => {
      const userId = 'user-1'
      const bucket = ensureTokenUsageBucket(userId)
      bucket.tokenOverride = 50000
      
      const limit = getPlanTokenLimits('hobby', userId)
      expect(limit).toBe(50000)
    })
  })

  describe('Admin Model Management', () => {
    let opencodeModels
    let publicModels

    beforeEach(() => {
      opencodeModels = [
        { id: 'model-1', name: 'gpt-4', label: 'GPT-4', tier: 'premium' },
        { id: 'model-2', name: 'claude-3', label: 'Claude 3', tier: 'premium' }
      ]
      publicModels = [
        { id: 'model-3', name: 'gpt-3.5', label: 'GPT-3.5', tier: 'standard' }
      ]
    })

    function normalizeTier(tier) {
      const validTiers = ['free', 'standard', 'premium', 'enterprise']
      return validTiers.includes(tier) ? tier : 'standard'
    }

    function getTierMultiplier(tier) {
      const multipliers = {
        free: 0.5,
        standard: 1,
        premium: 2,
        enterprise: 5
      }
      return multipliers[tier] || 1
    }

    test('lists opencode models', () => {
      expect(opencodeModels.length).toBe(2)
      expect(opencodeModels[0].name).toBe('gpt-4')
    })

    test('lists public models', () => {
      expect(publicModels.length).toBe(1)
      expect(publicModels[0].name).toBe('gpt-3.5')
    })

    test('normalizes tier values', () => {
      expect(normalizeTier('free')).toBe('free')
      expect(normalizeTier('premium')).toBe('premium')
      expect(normalizeTier('invalid')).toBe('standard')
    })

    test('gets tier multipliers', () => {
      expect(getTierMultiplier('free')).toBe(0.5)
      expect(getTierMultiplier('standard')).toBe(1)
      expect(getTierMultiplier('premium')).toBe(2)
      expect(getTierMultiplier('enterprise')).toBe(5)
    })

    test('adds new model to opencode', () => {
      const newModel = {
        id: 'model-4',
        name: 'llama-3',
        label: 'LLaMA 3',
        tier: 'standard'
      }
      opencodeModels.push(newModel)
      
      expect(opencodeModels.length).toBe(3)
      expect(opencodeModels.find(m => m.id === 'model-4')).toBeDefined()
    })

    test('updates existing model', () => {
      const model = opencodeModels.find(m => m.id === 'model-1')
      model.label = 'GPT-4 Turbo'
      
      expect(model.label).toBe('GPT-4 Turbo')
    })

    test('deletes model', () => {
      const index = opencodeModels.findIndex(m => m.id === 'model-1')
      opencodeModels.splice(index, 1)
      
      expect(opencodeModels.length).toBe(1)
      expect(opencodeModels.find(m => m.id === 'model-1')).toBeUndefined()
    })

    test('reorders models', () => {
      const newOrder = [
        { id: 'model-2' },
        { id: 'model-1' }
      ]
      
      const reordered = []
      newOrder.forEach(m => {
        const model = opencodeModels.find(pm => pm.id === m.id)
        if (model) reordered.push(model)
      })
      
      opencodeModels = reordered
      
      expect(opencodeModels[0].id).toBe('model-2')
      expect(opencodeModels[1].id).toBe('model-1')
    })
  })

  describe('Admin Authentication Validation', () => {
    function checkHoneypot(body) {
      return !!(body && body.website)
    }

    function isValidEmail(email) {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
      return emailRegex.test(email)
    }

    test('detects honeypot field', () => {
      expect(checkHoneypot({ website: 'spam' })).toBe(true)
      expect(checkHoneypot({ website: '' })).toBe(true)
      expect(checkHoneypot({ other: 'value' })).toBe(false)
      expect(checkHoneypot({})).toBe(false)
    })

    test('validates email format', () => {
      expect(isValidEmail('test@example.com')).toBe(true)
      expect(isValidEmail('user.name@domain.co.uk')).toBe(true)
      expect(isValidEmail('invalid')).toBe(false)
      expect(isValidEmail('no@domain')).toBe(false)
      expect(isValidEmail('@nodomain.com')).toBe(false)
    })
  })
})
