/**
 * Cloudflare PoP to Physical Infrastructure Mapping Service
 */

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

export interface PoPLocation {
  city: string
  country: string
  coordinates: { lat: number; lng: number }
  popId: string
}

export interface PoPMapping {
  id: string
  popId: string
  popLocation: PoPLocation
  primaryDatacenterId: string
  regionId: string
  tunnelConfigurations: TunnelConfiguration[]
  routingRules: RoutingRules
  createdAt: Date
  updatedAt: Date
}

export interface TunnelConfiguration {
  tunnelId: string
  popId: string
  targetDatacenterId: string
  tunnelType: 'PRIMARY' | 'BACKUP' | 'LOAD_BALANCED'
  healthStatus: 'HEALTHY' | 'DEGRADED' | 'DOWN'
  endpoint: string
  healthCheck: HealthCheckConfig
}

export interface RoutingRules {
  latencyThreshold: number // ms
  failoverThreshold: number // ms
  loadBalancing: 'ROUND_ROBIN' | 'LEAST_CONNECTIONS' | 'GEOGRAPHIC'
  failoverEnabled: boolean
}

export interface HealthCheckConfig {
  endpoint: string
  interval: number // seconds
  timeout: number // seconds
  failureThreshold: number
}

class PoPMappingService {
  /**
   * Map Cloudflare PoP to optimal datacenter
   */
  async mapPoPToRegion(
    context: Context,
    popLocation: PoPLocation
  ): Promise<PoPMapping> {
    logger.info('Mapping PoP to region', { popId: popLocation.popId })

    // Find nearest datacenter
    const nearestDatacenter = await this.findNearestDatacenter(popLocation)

    // Create tunnel configuration
    const tunnelConfig = await this.createTunnelConfiguration(
      popLocation.popId,
      nearestDatacenter.id
    )

    // Store mapping
    const db = getDb()
    const result = await db.query(
      `INSERT INTO pop_mappings (
        pop_id, pop_location, primary_datacenter_id, region_id,
        tunnel_configuration, routing_rules
      ) VALUES ($1, $2, $3, $4, $5, $6)
      RETURNING *`,
      [
        popLocation.popId,
        JSON.stringify(popLocation),
        nearestDatacenter.id,
        nearestDatacenter.regionId,
        JSON.stringify(tunnelConfig),
        JSON.stringify({
          latencyThreshold: 50,
          failoverThreshold: 100,
          loadBalancing: 'GEOGRAPHIC',
          failoverEnabled: true,
        }),
      ]
    )

    return this.mapPoPMapping(result.rows[0])
  }

  /**
   * Find nearest datacenter to PoP
   */
  private async findNearestDatacenter(
    popLocation: PoPLocation
  ): Promise<{ id: string; regionId: string; location: any }> {
    const db = getDb()

    // Get all datacenters with their locations
    const result = await db.query(
      `SELECT id, region_id, location, type FROM datacenters WHERE status = 'ACTIVE'`
    )

    let nearest: any = null
    let minDistance = Infinity

    for (const dc of result.rows) {
      const distance = this.calculateDistance(
        popLocation.coordinates,
        dc.location.coordinates
      )

      if (distance < minDistance) {
        minDistance = distance
        nearest = dc
      }
    }

    if (!nearest) {
      throw new Error('No active datacenter found')
    }

    return {
      id: nearest.id,
      regionId: nearest.region_id,
      location: nearest.location,
    }
  }

  /**
   * Calculate distance between two coordinates (Haversine formula)
   */
  private calculateDistance(
    coord1: { lat: number; lng: number },
    coord2: { lat: number; lng: number }
  ): number {
    const R = 6371 // Earth's radius in km
    const dLat = this.toRad(coord2.lat - coord1.lat)
    const dLon = this.toRad(coord2.lng - coord1.lng)

    const a =
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(this.toRad(coord1.lat)) *
        Math.cos(this.toRad(coord2.lat)) *
        Math.sin(dLon / 2) *
        Math.sin(dLon / 2)

    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
    return R * c
  }

  private toRad(degrees: number): number {
    return (degrees * Math.PI) / 180
  }

  /**
   * Create tunnel configuration
   */
  private async createTunnelConfiguration(
    popId: string,
    datacenterId: string
  ): Promise<TunnelConfiguration> {
    // In production, this would call Cloudflare API to create tunnel
    return {
      tunnelId: `tunnel-${popId}-${datacenterId}`,
      popId,
      targetDatacenterId: datacenterId,
      tunnelType: 'PRIMARY',
      healthStatus: 'HEALTHY',
      endpoint: `https://${datacenterId}.phoenix.io`,
      healthCheck: {
        endpoint: `/health`,
        interval: 30,
        timeout: 5,
        failureThreshold: 3,
      },
    }
  }

  /**
   * Get optimal datacenter for PoP
   */
  async getOptimalDatacenter(
    context: Context,
    popId: string
  ): Promise<string> {
    const db = getDb()
    const result = await db.query(
      `SELECT primary_datacenter_id FROM pop_mappings WHERE pop_id = $1`,
      [popId]
    )

    if (result.rows.length === 0) {
      throw new Error(`PoP mapping not found: ${popId}`)
    }

    return result.rows[0].primary_datacenter_id
  }

  /**
   * Update routing configuration
   */
  async updateRouting(
    context: Context,
    popId: string,
    routingRules: RoutingRules
  ): Promise<void> {
    const db = getDb()
    await db.query(
      `UPDATE pop_mappings SET routing_rules = $1, updated_at = NOW() WHERE pop_id = $2`,
      [JSON.stringify(routingRules), popId]
    )

    logger.info('Routing updated', { popId, routingRules })
  }

  /**
   * Get all PoP mappings
   */
  async getPoPMappings(context: Context): Promise<PoPMapping[]> {
    const db = getDb()
    const result = await db.query(`SELECT * FROM pop_mappings ORDER BY pop_id`)
    return result.rows.map(this.mapPoPMapping)
  }

  /**
   * Get PoP mapping by ID
   */
  async getPoPMapping(context: Context, popId: string): Promise<PoPMapping | null> {
    const db = getDb()
    const result = await db.query(
      `SELECT * FROM pop_mappings WHERE pop_id = $1`,
      [popId]
    )
    if (result.rows.length === 0) return null
    return this.mapPoPMapping(result.rows[0])
  }

  private mapPoPMapping(row: any): PoPMapping {
    return {
      id: row.id,
      popId: row.pop_id,
      popLocation: row.pop_location,
      primaryDatacenterId: row.primary_datacenter_id,
      regionId: row.region_id,
      tunnelConfigurations: row.tunnel_configuration || [],
      routingRules: row.routing_rules || {},
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    }
  }
}

export const popMappingService = new PoPMappingService()

