const { describe, test, expect, beforeEach, afterEach } = require('bun:test')
const crypto = require('crypto')
const path = require('path')
const fs = require('fs')
const Database = require('better-sqlite3')

const testDbPath = path.join(__dirname, '.test-audit.db')

let testDb

function createTestSchema(db) {
  db.exec(`
    CREATE TABLE IF NOT EXISTS audit_log (
      id TEXT PRIMARY KEY,
      user_id TEXT,
      event_type TEXT NOT NULL,
      event_data TEXT,
      ip_address TEXT,
      user_agent TEXT,
      success INTEGER DEFAULT 1,
      error_message TEXT,
      created_at INTEGER NOT NULL
    )
  `)
}

describe('Audit Repository', () => {
  beforeEach(() => {
    if (fs.existsSync(testDbPath)) {
      fs.unlinkSync(testDbPath)
    }
    
    testDb = new Database(testDbPath)
    createTestSchema(testDb)
  })

  afterEach(() => {
    if (testDb) {
      testDb.close()
    }
    if (fs.existsSync(testDbPath)) {
      fs.unlinkSync(testDbPath)
    }
  })

  describe('Audit Event Logging', () => {
    test('logs a basic audit event', () => {
      const now = Date.now()
      const eventId = crypto.randomUUID()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, event_type, created_at)
        VALUES (?, ?, ?)
      `).run(eventId, 'user.login', now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get(eventId)
      
      expect(event).toBeDefined()
      expect(event.event_type).toBe('user.login')
      expect(event.success).toBe(1)
    })

    test('logs event with user ID', () => {
      const now = Date.now()
      const eventId = crypto.randomUUID()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, created_at)
        VALUES (?, ?, ?, ?)
      `).run(eventId, 'user-123', 'user.logout', now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get(eventId)
      
      expect(event.user_id).toBe('user-123')
    })

    test('logs event with all metadata', () => {
      const now = Date.now()
      const eventId = crypto.randomUUID()
      const eventData = JSON.stringify({ method: 'password', rememberMe: true })
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, event_data, ip_address, user_agent, success, created_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
      `).run(eventId, 'user-456', 'user.login', eventData, '192.168.1.1', 'Mozilla/5.0', 1, now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get(eventId)
      
      expect(event.ip_address).toBe('192.168.1.1')
      expect(event.user_agent).toBe('Mozilla/5.0')
      const parsedData = JSON.parse(event.event_data)
      expect(parsedData.method).toBe('password')
    })

    test('logs failed event with error message', () => {
      const now = Date.now()
      const eventId = crypto.randomUUID()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, success, error_message, created_at)
        VALUES (?, ?, ?, ?, ?, ?)
      `).run(eventId, 'user-789', 'user.login', 0, 'Invalid password', now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get(eventId)
      
      expect(event.success).toBe(0)
      expect(event.error_message).toBe('Invalid password')
    })
  })

  describe('Event Types', () => {
    test('logs authentication events', () => {
      const now = Date.now()
      const eventTypes = ['user.login', 'user.logout', 'user.login.failed', 'user.password.reset', 'user.password.change']
      
      eventTypes.forEach((type, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`auth-${i}`, type, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE event_type LIKE ?').all('user.%')
      
      expect(events.length).toBe(5)
    })

    test('logs session events', () => {
      const now = Date.now()
      const eventTypes = ['session.create', 'session.refresh', 'session.destroy', 'session.expired']
      
      eventTypes.forEach((type, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`session-${i}`, type, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE event_type LIKE ?').all('session.%')
      
      expect(events.length).toBe(4)
    })

    test('logs payment events', () => {
      const now = Date.now()
      const eventTypes = ['payment.created', 'payment.succeeded', 'payment.failed', 'subscription.created', 'subscription.cancelled']
      
      eventTypes.forEach((type, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`payment-${i}`, type, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE event_type LIKE ?').all('payment.%')
      
      expect(events.length).toBe(3)
    })

    test('logs security events', () => {
      const now = Date.now()
      const eventTypes = ['security.2fa.enabled', 'security.2fa.disabled', 'security.token.blacklisted', 'security.suspicious.activity']
      
      eventTypes.forEach((type, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`security-${i}`, type, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE event_type LIKE ?').all('security.%')
      
      expect(events.length).toBe(4)
    })

    test('logs admin events', () => {
      const now = Date.now()
      const eventTypes = ['admin.login', 'admin.user.updated', 'admin.plan.changed', 'admin.settings.updated']
      
      eventTypes.forEach((type, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`admin-${i}`, type, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE event_type LIKE ?').all('admin.%')
      
      expect(events.length).toBe(4)
    })
  })

  describe('Query Operations', () => {
    test('gets user audit log with pagination', () => {
      const now = Date.now()
      
      for (let i = 0; i < 25; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, user_id, event_type, created_at)
          VALUES (?, ?, ?, ?)
        `).run(`page-${i}`, 'user-page', `event.${i}`, now + i)
      }
      
      const page1 = testDb.prepare(`
        SELECT * FROM audit_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 10 OFFSET 0
      `).all('user-page')
      
      const page2 = testDb.prepare(`
        SELECT * FROM audit_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 10 OFFSET 10
      `).all('user-page')
      
      expect(page1.length).toBe(10)
      expect(page2.length).toBe(10)
      expect(page1[0].created_at).toBeGreaterThan(page1[1].created_at)
    })

    test('filters by event type', () => {
      const now = Date.now()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, created_at)
        VALUES (?, ?, ?, ?)
      `).run('filter-1', 'user-filter', 'user.login', now)
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, created_at)
        VALUES (?, ?, ?, ?)
      `).run('filter-2', 'user-filter', 'user.logout', now + 1)
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, created_at)
        VALUES (?, ?, ?, ?)
      `).run('filter-3', 'user-filter', 'user.login', now + 2)
      
      const loginEvents = testDb.prepare(`
        SELECT * FROM audit_log WHERE user_id = ? AND event_type = ? ORDER BY created_at DESC
      `).all('user-filter', 'user.login')
      
      expect(loginEvents.length).toBe(2)
    })

    test('gets recent audit events', () => {
      const now = Date.now()
      
      for (let i = 0; i < 50; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`recent-${i}`, `event.${i}`, now + i)
      }
      
      const recent = testDb.prepare(`
        SELECT * FROM audit_log ORDER BY created_at DESC LIMIT 20
      `).all()
      
      expect(recent.length).toBe(20)
      expect(recent[0].created_at).toBeGreaterThan(recent[19].created_at)
    })

    test('gets recent events filtered by type', () => {
      const now = Date.now()
      
      for (let i = 0; i < 10; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`type-a-${i}`, 'typeA', now + i)
        
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`type-b-${i}`, 'typeB', now + 100 + i)
      }
      
      const typeAEvents = testDb.prepare(`
        SELECT * FROM audit_log WHERE event_type = ? ORDER BY created_at DESC LIMIT 5
      `).all('typeA')
      
      expect(typeAEvents.length).toBe(5)
      typeAEvents.forEach(e => expect(e.event_type).toBe('typeA'))
    })
  })

  describe('Event Data Handling', () => {
    test('stores complex event data as JSON', () => {
      const now = Date.now()
      const eventData = {
        loginMethod: 'oauth',
        provider: 'google',
        deviceInfo: {
          browser: 'Chrome',
          os: 'Windows',
          device: 'Desktop'
        },
        location: {
          country: 'US',
          city: 'New York'
        }
      }
      
      testDb.prepare(`
        INSERT INTO audit_log (id, event_type, event_data, created_at)
        VALUES (?, ?, ?, ?)
      `).run('complex-1', 'user.login', JSON.stringify(eventData), now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get('complex-1')
      const parsedData = JSON.parse(event.event_data)
      
      expect(parsedData.loginMethod).toBe('oauth')
      expect(parsedData.deviceInfo.browser).toBe('Chrome')
      expect(parsedData.location.country).toBe('US')
    })

    test('handles null event data', () => {
      const now = Date.now()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, event_type, event_data, created_at)
        VALUES (?, ?, ?, ?)
      `).run('null-data', 'user.logout', null, now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get('null-data')
      
      expect(event.event_data).toBeNull()
    })

    test('handles empty event data', () => {
      const now = Date.now()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, event_type, event_data, created_at)
        VALUES (?, ?, ?, ?)
      `).run('empty-data', 'user.logout', '{}', now)
      
      const event = testDb.prepare('SELECT * FROM audit_log WHERE id = ?').get('empty-data')
      const parsedData = JSON.parse(event.event_data)
      
      expect(parsedData).toEqual({})
    })
  })

  describe('IP and User Agent Tracking', () => {
    test('stores IP address', () => {
      const now = Date.now()
      const ipAddresses = ['192.168.1.1', '10.0.0.1', '172.16.0.1', '203.0.113.42', '2001:db8::1']
      
      ipAddresses.forEach((ip, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, ip_address, created_at)
          VALUES (?, ?, ?, ?)
        `).run(`ip-${i}`, 'user.login', ip, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE id LIKE ?').all('ip-%')
      
      expect(events.length).toBe(5)
      expect(events[0].ip_address).toBe('192.168.1.1')
      expect(events[4].ip_address).toBe('2001:db8::1')
    })

    test('stores user agent', () => {
      const now = Date.now()
      const userAgents = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1.15',
        'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Mobile/15E148'
      ]
      
      userAgents.forEach((ua, i) => {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, user_agent, created_at)
          VALUES (?, ?, ?, ?)
        `).run(`ua-${i}`, 'user.login', ua, now + i)
      })
      
      const events = testDb.prepare('SELECT * FROM audit_log WHERE id LIKE ?').all('ua-%')
      
      expect(events.length).toBe(3)
      expect(events[0].user_agent).toContain('Chrome')
      expect(events[1].user_agent).toContain('Safari')
      expect(events[2].user_agent).toContain('iPhone')
    })

    test('detects login from new IP', () => {
      const now = Date.now()
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, ip_address, created_at)
        VALUES (?, ?, ?, ?, ?)
      `).run('new-ip-1', 'user-newip', 'user.login', '192.168.1.1', now)
      
      testDb.prepare(`
        INSERT INTO audit_log (id, user_id, event_type, ip_address, created_at)
        VALUES (?, ?, ?, ?, ?)
      `).run('new-ip-2', 'user-newip', 'user.login', '10.0.0.1', now + 1)
      
      const uniqueIps = testDb.prepare(`
        SELECT DISTINCT ip_address FROM audit_log WHERE user_id = ?
      `).all('user-newip')
      
      expect(uniqueIps.length).toBe(2)
    })
  })

  describe('Success/Failure Tracking', () => {
    test('tracks successful events', () => {
      const now = Date.now()
      
      for (let i = 0; i < 5; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, success, created_at)
          VALUES (?, ?, ?, ?)
        `).run(`success-${i}`, 'user.login', 1, now + i)
      }
      
      const successful = testDb.prepare(`
        SELECT COUNT(*) as count FROM audit_log WHERE event_type = ? AND success = 1
      `).get('user.login')
      
      expect(successful.count).toBe(5)
    })

    test('tracks failed events', () => {
      const now = Date.now()
      
      for (let i = 0; i < 3; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, success, error_message, created_at)
          VALUES (?, ?, ?, ?, ?)
        `).run(`fail-${i}`, 'user.login', 0, 'Invalid credentials', now + i)
      }
      
      const failed = testDb.prepare(`
        SELECT COUNT(*) as count FROM audit_log WHERE event_type = ? AND success = 0
      `).get('user.login')
      
      expect(failed.count).toBe(3)
    })

    test('calculates success rate', () => {
      const now = Date.now()
      
      for (let i = 0; i < 8; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, success, created_at)
          VALUES (?, ?, ?, ?)
        `).run(`rate-${i}`, 'user.login', i < 6 ? 1 : 0, now + i)
      }
      
      const stats = testDb.prepare(`
        SELECT 
          COUNT(*) as total,
          SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successful
        FROM audit_log WHERE event_type = ?
      `).get('user.login')
      
      const successRate = stats.successful / stats.total
      expect(successRate).toBe(0.75)
    })
  })

  describe('Time-based Queries', () => {
    test('queries events within time range', () => {
      const baseTime = Date.now()
      
      for (let i = 0; i < 24; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`time-${i}`, 'user.login', baseTime + (i * 3600000))
      }
      
      const hourAgo = baseTime + (20 * 3600000)
      const events = testDb.prepare(`
        SELECT * FROM audit_log WHERE created_at >= ? ORDER BY created_at DESC
      `).all(hourAgo)
      
      expect(events.length).toBe(4)
    })

    test('groups events by hour', () => {
      const baseTime = Date.now()
      
      for (let i = 0; i < 12; i++) {
        testDb.prepare(`
          INSERT INTO audit_log (id, event_type, created_at)
          VALUES (?, ?, ?)
        `).run(`hour-${i}`, 'user.login', baseTime + (i * 1800000))
      }
      
      const events = testDb.prepare('SELECT * FROM audit_log ORDER BY created_at').all()
      
      expect(events.length).toBe(12)
    })
  })
})
