/**
 * Billing Service Tests
 */

import { describe, it, expect, beforeEach, vi } from 'vitest'
import { billingService } from '../../services/billing'
import { getDb } from '../../db'

// Mock database
vi.mock('../../db', () => ({
  getDb: vi.fn(),
}))

// Mock logger
vi.mock('../../lib/logger', () => ({
  logger: {
    info: vi.fn(),
    error: vi.fn(),
    warn: vi.fn(),
  },
}))

// Mock blockchain service
vi.mock('../../services/blockchain', () => ({
  blockchainService: {
    recordUsage: vi.fn().mockResolvedValue(undefined),
  },
}))

describe('BillingService', () => {
  let mockDb: any

  beforeEach(() => {
    mockDb = {
      query: vi.fn(),
    }
    vi.mocked(getDb).mockReturnValue(mockDb as never)
  })

  describe('recordUsage', () => {
    it('should record usage for a tenant', async () => {
      const usageRecord = {
        tenantId: 'tenant-1',
        resourceId: 'resource-1',
        resourceType: 'VM',
        metricType: 'CPU_USAGE',
        quantity: 100,
        unit: 'percent',
        cost: 10.50,
        currency: 'USD',
        timestamp: new Date(),
      }

      mockDb.query.mockResolvedValueOnce({
        rows: [{
          id: 'usage-1',
          ...usageRecord,
        }],
      })

      const result = await billingService.recordUsage(usageRecord)

      expect(result).toBeDefined()
      expect(result.tenantId).toBe('tenant-1')
      expect(mockDb.query).toHaveBeenCalled()
    })
  })

  describe('getUsageReport', () => {
    it('should generate usage report', async () => {
      const tenantId = 'tenant-1'
      const timeRange = {
        start: new Date('2024-01-01'),
        end: new Date('2024-01-31'),
      }

      mockDb.query.mockResolvedValueOnce({
        rows: [
          {
            resource_id: 'resource-1',
            resource_name: 'VM-1',
            resource_type: 'VM',
            cost: 100.50,
            quantity: 100,
            unit: 'hours',
          },
        ],
      })

      mockDb.query.mockResolvedValueOnce({
        rows: [
          {
            metric_type: 'CPU_USAGE',
            cost: 50.25,
            quantity: 50,
            unit: 'percent',
          },
        ],
      })

      mockDb.query.mockResolvedValueOnce({
        rows: [{
          total_cost: 150.75,
        }],
      })

      const report = await billingService.getUsageReport(
        tenantId,
        timeRange,
        'DAY'
      )

      expect(report).toBeDefined()
      expect(report.tenantId).toBe(tenantId)
      expect(report.totalCost).toBeGreaterThan(0)
    })
  })

  describe('createBudget', () => {
    it('should create a budget', async () => {
      const tenantId = 'tenant-1'
      const budgetInput = {
        name: 'Monthly Budget',
        amount: 1000,
        currency: 'USD',
        period: 'MONTHLY' as const,
        startDate: new Date('2024-01-01'),
        alertThresholds: [0.5, 0.75, 0.9],
      }

      mockDb.query.mockResolvedValueOnce({
        rows: [{
          id: 'budget-1',
          tenant_id: tenantId,
          name: budgetInput.name,
          amount: budgetInput.amount,
          currency: budgetInput.currency,
          period: budgetInput.period,
          start_date: budgetInput.startDate,
          end_date: null,
          alert_thresholds: JSON.stringify(budgetInput.alertThresholds),
          filters: '{}',
          current_spend: 0,
          remaining: budgetInput.amount,
          created_at: new Date(),
          updated_at: new Date(),
        }],
      })

      const budget = await billingService.createBudget(tenantId, budgetInput)

      expect(budget).toBeDefined()
      expect(budget.name).toBe('Monthly Budget')
      expect(budget.amount).toBe(1000)
    })
  })

  describe('createInvoice', () => {
    it('should generate an invoice', async () => {
      const tenantId = 'tenant-1'
      const billingPeriodStart = new Date('2024-01-01')
      const billingPeriodEnd = new Date('2024-01-31')

      // Mock usage records
      mockDb.query.mockResolvedValueOnce({
        rows: [
          {
            id: 'usage-1',
            resource_id: 'resource-1',
            cost: 100.50,
            quantity: 100,
            unit: 'hours',
          },
        ],
      })

      // Mock invoice creation
      mockDb.query.mockResolvedValueOnce({
        rows: [{
          id: 'invoice-1',
          tenant_id: tenantId,
          invoice_number: 'INV-2024-001',
          billing_period_start: billingPeriodStart,
          billing_period_end: billingPeriodEnd,
          subtotal: 100.50,
          tax: 10.05,
          total: 110.55,
          currency: 'USD',
          status: 'PENDING',
          line_items: JSON.stringify([
            {
              id: 'item-1',
              description: 'VM Usage',
              quantity: 100,
              unitPrice: 1.005,
              total: 100.50,
            },
          ]),
          created_at: new Date(),
          updated_at: new Date(),
        }],
      })

      // Mock getInvoice
      mockDb.query.mockResolvedValueOnce({
        rows: [{
          id: 'invoice-1',
          tenant_id: tenantId,
          invoice_number: 'INV-2024-001',
          billing_period_start: billingPeriodStart,
          billing_period_end: billingPeriodEnd,
          subtotal: 100.50,
          tax: 10.05,
          total: 110.55,
          currency: 'USD',
          status: 'PENDING',
          line_items: JSON.stringify([
            {
              id: 'item-1',
              description: 'VM Usage',
              quantity: 100,
              unitPrice: 1.005,
              total: 100.50,
            },
          ]),
          created_at: new Date(),
          updated_at: new Date(),
        }],
      })

      const invoice = await billingService.createInvoice(
        tenantId,
        billingPeriodStart,
        billingPeriodEnd
      )

      expect(invoice).toBeDefined()
      expect(invoice.tenantId).toBe(tenantId)
      expect(invoice.total).toBeGreaterThan(0)
      expect(invoice.lineItems.length).toBeGreaterThan(0)
    })
  })
})

