/**
 * Phoenix Observability Stack Service
 * Distributed tracing, structured logs, SLOs, correlation IDs
 */

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

export interface Trace {
  traceId: string
  correlationId: string
  spans: Span[]
  startTime: Date
  endTime: Date
  duration: number
}

export interface Span {
  spanId: string
  traceId: string
  parentSpanId: string | null
  serviceName: string
  operationName: string
  startTime: Date
  endTime: Date
  duration: number
  tags: Record<string, any>
  logs: LogEntry[]
}

export interface LogEntry {
  timestamp: Date
  level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
  message: string
  correlationId: string
  serviceName: string
  metadata: Record<string, any>
}

export interface SLO {
  sloId: string
  serviceName: string
  metricName: string
  target: number
  window: string
  currentValue: number
  status: 'HEALTHY' | 'WARNING' | 'BREACHED'
}

class ObservabilityService {
  /**
   * Create a trace
   */
  async createTrace(correlationId: string): Promise<Trace> {
    const db = getDb()
    
    const traceId = this.generateTraceId()
    
    const result = await db.query(
      `INSERT INTO traces (trace_id, correlation_id, start_time)
       VALUES ($1, $2, NOW())
       RETURNING *`,
      [traceId, correlationId]
    )

    logger.info('Trace created', { traceId, correlationId })
    return {
      traceId,
      correlationId,
      spans: [],
      startTime: result.rows[0].start_time,
      endTime: result.rows[0].start_time,
      duration: 0
    }
  }

  /**
   * Add span to trace
   */
  async addSpan(
    traceId: string,
    serviceName: string,
    operationName: string,
    parentSpanId: string | null,
    tags: Record<string, any> = {}
  ): Promise<Span> {
    const db = getDb()
    
    const spanId = this.generateSpanId()
    const startTime = new Date()
    
    const result = await db.query(
      `INSERT INTO spans (
        span_id, trace_id, parent_span_id, service_name, operation_name, start_time, tags
      ) VALUES ($1, $2, $3, $4, $5, $6, $7)
      RETURNING *`,
      [
        spanId,
        traceId,
        parentSpanId,
        serviceName,
        operationName,
        startTime,
        JSON.stringify(tags)
      ]
    )

    return {
      spanId,
      traceId,
      parentSpanId,
      serviceName,
      operationName,
      startTime,
      endTime: startTime,
      duration: 0,
      tags,
      logs: []
    }
  }

  /**
   * Complete a span
   */
  async completeSpan(spanId: string, endTime?: Date): Promise<void> {
    const db = getDb()
    
    const span = await db.query(
      `SELECT * FROM spans WHERE span_id = $1`,
      [spanId]
    )

    if (span.rows.length === 0) {
      throw new Error('Span not found')
    }

    const finishTime = endTime || new Date()
    const duration = finishTime.getTime() - span.rows[0].start_time.getTime()

    await db.query(
      `UPDATE spans SET end_time = $1, duration = $2 WHERE span_id = $3`,
      [finishTime, duration, spanId]
    )
  }

  /**
   * Log with correlation ID
   */
  async log(
    level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR',
    message: string,
    correlationId: string,
    serviceName: string,
    metadata: Record<string, any> = {}
  ): Promise<void> {
    const db = getDb()
    
    await db.query(
      `INSERT INTO structured_logs (
        level, message, correlation_id, service_name, metadata, timestamp
      ) VALUES ($1, $2, $3, $4, $5, NOW())`,
      [level, message, correlationId, serviceName, JSON.stringify(metadata)]
    )

    logger[level.toLowerCase()](message, { correlationId, serviceName, ...metadata })
  }

  /**
   * Get SLO status
   */
  async getSLOStatus(serviceName: string, metricName: string): Promise<SLO | null> {
    const db = getDb()
    
    const result = await db.query(
      `SELECT * FROM slos WHERE service_name = $1 AND metric_name = $2`,
      [serviceName, metricName]
    )

    if (result.rows.length === 0) {
      return null
    }

    const row = result.rows[0]
    const currentValue = await this.getCurrentMetricValue(serviceName, metricName)
    const status = this.calculateSLOStatus(row.target, currentValue)

    return {
      sloId: row.id,
      serviceName: row.service_name,
      metricName: row.metric_name,
      target: parseFloat(row.target),
      window: row.window,
      currentValue,
      status
    }
  }

  private async getCurrentMetricValue(serviceName: string, metricName: string): Promise<number> {
    // Placeholder - would query actual metrics
    return 0.99 // Example: 99% uptime
  }

  private calculateSLOStatus(target: number, current: number): 'HEALTHY' | 'WARNING' | 'BREACHED' {
    if (current >= target) return 'HEALTHY'
    if (current >= target * 0.95) return 'WARNING'
    return 'BREACHED'
  }

  private generateTraceId(): string {
    return `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }

  private generateSpanId(): string {
    return `span_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }
}

export const observabilityService = new ObservabilityService()
