/**
 * Phoenix Ledger Service
 * Double-entry ledger with virtual accounts, holds, and multi-asset support
 */

import { getDb } from '../../db/index.js'
import { logger } from '../../lib/logger.js'

export interface JournalEntry {
  entryId: string
  timestamp: Date
  description: string
  correlationId: string
  lines: JournalLine[]
}

export interface JournalLine {
  accountRef: string
  debit: number
  credit: number
  asset: string
}

export interface VirtualAccount {
  subaccountId: string
  accountId: string
  currency: string
  asset: string
  labels: Record<string, string>
}

export interface Hold {
  holdId: string
  amount: number
  asset: string
  expiry: Date | null
  status: 'ACTIVE' | 'RELEASED' | 'EXPIRED'
}

export interface Balance {
  accountId: string
  subaccountId: string | null
  asset: string
  balance: number
}

class LedgerService {
  /**
   * Create a journal entry (idempotent via correlation_id)
   */
  async createJournalEntry(
    correlationId: string,
    description: string,
    lines: JournalLine[]
  ): Promise<JournalEntry> {
    const db = getDb()
    
    // Check idempotency
    const existing = await db.query(
      `SELECT * FROM journal_entries WHERE correlation_id = $1`,
      [correlationId]
    )
    
    if (existing.rows.length > 0) {
      logger.info('Journal entry already exists', { correlationId })
      return this.mapJournalEntry(existing.rows[0])
    }

    // Validate double-entry balance
    const totalDebits = lines.reduce((sum, line) => sum + line.debit, 0)
    const totalCredits = lines.reduce((sum, line) => sum + line.credit, 0)
    
    if (Math.abs(totalDebits - totalCredits) > 0.01) {
      throw new Error('Journal entry is not balanced')
    }

    // Create entry
    const result = await db.query(
      `INSERT INTO journal_entries (correlation_id, description, timestamp)
       VALUES ($1, $2, NOW())
       RETURNING *`,
      [correlationId, description]
    )

    const entryId = result.rows[0].id

    // Create journal lines
    for (const line of lines) {
      await db.query(
        `INSERT INTO journal_lines (entry_id, account_ref, debit, credit, asset)
         VALUES ($1, $2, $3, $4, $5)`,
        [entryId, line.accountRef, line.debit, line.credit, line.asset]
      )
    }

    logger.info('Journal entry created', { entryId, correlationId })
    return this.mapJournalEntry(result.rows[0])
  }

  /**
   * Create a hold (reserve)
   */
  async createHold(
    accountId: string,
    amount: number,
    asset: string,
    expiry: Date | null = null
  ): Promise<Hold> {
    const db = getDb()
    
    const result = await db.query(
      `INSERT INTO holds (account_id, amount, asset, expiry, status)
       VALUES ($1, $2, $3, $4, 'ACTIVE')
       RETURNING *`,
      [accountId, amount, asset, expiry]
    )

    logger.info('Hold created', { holdId: result.rows[0].id })
    return this.mapHold(result.rows[0])
  }

  /**
   * Get balance for account/subaccount
   */
  async getBalance(accountId: string, subaccountId?: string, asset?: string): Promise<Balance[]> {
    const db = getDb()
    
    // This would query a materialized view or compute from journal_lines
    const query = `
      SELECT 
        account_ref as account_id,
        asset,
        SUM(debit - credit) as balance
      FROM journal_lines
      WHERE account_ref = $1
      ${subaccountId ? 'AND account_ref LIKE $2' : ''}
      ${asset ? 'AND asset = $3' : ''}
      GROUP BY account_ref, asset
    `
    
    const params: any[] = [accountId]
    if (subaccountId) params.push(`${accountId}:${subaccountId}`)
    if (asset) params.push(asset)

    const result = await db.query(query, params)
    return result.rows.map(row => ({
      accountId: row.account_id,
      subaccountId: subaccountId || null,
      asset: row.asset,
      balance: parseFloat(row.balance)
    }))
  }

  private mapJournalEntry(row: any): JournalEntry {
    return {
      entryId: row.id,
      timestamp: row.timestamp,
      description: row.description,
      correlationId: row.correlation_id,
      lines: [] // Would be loaded separately
    }
  }

  private mapHold(row: any): Hold {
    return {
      holdId: row.id,
      amount: parseFloat(row.amount),
      asset: row.asset,
      expiry: row.expiry,
      status: row.status
    }
  }
}

export const ledgerService = new LedgerService()
