import { getDatabase } from '../../lib/db'
import {
  AdoptionHistoryRecord,
  RegionalBreakdownRecord,
  IXPBreakdownRecord,
  RegionalStats,
  IXPStats,
} from './types'

export class ASPADatabaseClient {
  async recordAdoptionSnapshot(
    sampleDate: Date,
    totalASNsSampled: number,
    ASNsWithASPA: number,
    regions: RegionalStats[],
    topAdopters: Array<{ asn: number; name: string; providers: number }>,
    metadata: Record<string, unknown> = {}
  ): Promise<{ id: number; sampleDate: Date }> {
    const db = getDatabase()
    const coveragePercentage = totalASNsSampled > 0 ? (ASNsWithASPA / totalASNsSampled) * 100 : 0

    const result = await db.query(
      `
      INSERT INTO aspa_adoption_history
        (sample_date, sampled_at, total_asns_sampled, asns_with_aspa, coverage_percentage, top_adopters, regions, metadata)
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
      ON CONFLICT (sample_date) DO UPDATE
      SET sampled_at = $2, total_asns_sampled = $3, asns_with_aspa = $4, coverage_percentage = $5, top_adopters = $6, regions = $7, metadata = $8
      RETURNING id, sample_date
      `,
      [
        sampleDate,
        new Date(),
        totalASNsSampled,
        ASNsWithASPA,
        coveragePercentage,
        JSON.stringify(topAdopters),
        JSON.stringify(regions),
        JSON.stringify(metadata),
      ]
    )

    if (result.rows.length === 0) {
      throw new Error('Failed to record ASPA adoption snapshot')
    }

    return {
      id: result.rows[0].id,
      sampleDate: new Date(result.rows[0].sample_date),
    }
  }

  async recordRegionalBreakdown(
    sampleDate: Date,
    regions: RegionalStats[]
  ): Promise<Array<{ id: number; region: string }>> {
    const db = getDatabase()
    const results: Array<{ id: number; region: string }> = []

    for (const region of regions) {
      const result = await db.query(
        `
        INSERT INTO aspa_adoption_by_region
          (sample_date, region, total_asns, asns_with_aspa, coverage_percentage, top_networks)
        VALUES ($1, $2, $3, $4, $5, $6)
        ON CONFLICT (sample_date, region) DO UPDATE
        SET total_asns = $3, asns_with_aspa = $4, coverage_percentage = $5, top_networks = $6
        RETURNING id, region
        `,
        [
          sampleDate,
          region.region,
          region.totalASNs,
          region.ASNsWithASPA,
          region.coveragePercentage,
          JSON.stringify(region.topNetworks),
        ]
      )

      if (result.rows.length > 0) {
        results.push({
          id: result.rows[0].id,
          region: result.rows[0].region,
        })
      }
    }

    return results
  }

  async recordIXPBreakdown(sampleDate: Date, ixps: IXPStats[]): Promise<Array<{ id: number; ixpName: string }>> {
    const db = getDatabase()
    const results: Array<{ id: number; ixpName: string }> = []

    for (const ixp of ixps) {
      const result = await db.query(
        `
        INSERT INTO aspa_adoption_by_ixp
          (sample_date, ixp_id, ixp_name, participant_count, participants_with_aspa, coverage_percentage)
        VALUES ($1, $2, $3, $4, $5, $6)
        ON CONFLICT (sample_date, ixp_id) DO UPDATE
        SET participant_count = $4, participants_with_aspa = $5, coverage_percentage = $6
        RETURNING id, ixp_name
        `,
        [sampleDate, ixp.ixpId, ixp.ixpName, ixp.participantCount, ixp.participantsWithASPA, ixp.coveragePercentage]
      )

      if (result.rows.length > 0) {
        results.push({
          id: result.rows[0].id,
          ixpName: result.rows[0].ixp_name,
        })
      }
    }

    return results
  }

  async getAdoptionHistory(days: number = 90): Promise<AdoptionHistoryRecord[]> {
    const db = getDatabase()
    const cutoffDate = new Date()
    cutoffDate.setDate(cutoffDate.getDate() - days)

    const result = await db.query(
      `
      SELECT id, sample_date, sampled_at, total_asns_sampled, asns_with_aspa, coverage_percentage,
             top_adopters, regions, sample_method, metadata
      FROM aspa_adoption_history
      WHERE sample_date >= $1
      ORDER BY sample_date DESC
      LIMIT 100
      `,
      [cutoffDate]
    )

    return result.rows.map((row: any) => ({
      id: row.id,
      sampleDate: new Date(row.sample_date),
      sampledAt: new Date(row.sampled_at),
      totalASNsSampled: row.total_asns_sampled,
      ASNsWithASPA: row.asns_with_aspa,
      coveragePercentage: row.coverage_percentage,
      adoptionRateChange: undefined,
      topAdopters: JSON.parse(row.top_adopters || '[]'),
      regions: JSON.parse(row.regions || '[]'),
      sampleMethod: row.sample_method,
      metadata: JSON.parse(row.metadata || '{}'),
    }))
  }

  async getRegionalHistory(region: string, days: number = 90): Promise<RegionalBreakdownRecord[]> {
    const db = getDatabase()
    const cutoffDate = new Date()
    cutoffDate.setDate(cutoffDate.getDate() - days)

    const result = await db.query(
      `
      SELECT id, sample_date, region, total_asns, asns_with_aspa, coverage_percentage, top_networks
      FROM aspa_adoption_by_region
      WHERE region = $1 AND sample_date >= $2
      ORDER BY sample_date DESC
      LIMIT 100
      `,
      [region, cutoffDate]
    )

    return result.rows.map((row: any) => ({
      id: row.id,
      sampleDate: new Date(row.sample_date),
      region: row.region,
      totalASNs: row.total_asns,
      ASNsWithASPA: row.asns_with_aspa,
      coveragePercentage: row.coverage_percentage,
      topNetworks: JSON.parse(row.top_networks || '[]'),
    }))
  }

  async getIXPHistory(ixpId: number, days: number = 90): Promise<IXPBreakdownRecord[]> {
    const db = getDatabase()
    const cutoffDate = new Date()
    cutoffDate.setDate(cutoffDate.getDate() - days)

    const result = await db.query(
      `
      SELECT id, sample_date, ixp_id, ixp_name, participant_count, participants_with_aspa, coverage_percentage
      FROM aspa_adoption_by_ixp
      WHERE ixp_id = $1 AND sample_date >= $2
      ORDER BY sample_date DESC
      LIMIT 100
      `,
      [ixpId, cutoffDate]
    )

    return result.rows.map((row: any) => ({
      id: row.id,
      sampleDate: new Date(row.sample_date),
      ixpId: row.ixp_id,
      ixpName: row.ixp_name,
      participantCount: row.participant_count,
      participantsWithASPA: row.participants_with_aspa,
      coveragePercentage: row.coverage_percentage,
    }))
  }

  async getLatestSnapshot(): Promise<AdoptionHistoryRecord | null> {
    const db = getDatabase()

    const result = await db.query(
      `
      SELECT id, sample_date, sampled_at, total_asns_sampled, asns_with_aspa, coverage_percentage,
             top_adopters, regions, sample_method, metadata
      FROM aspa_adoption_history
      ORDER BY sample_date DESC
      LIMIT 1
      `
    )

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

    const row = result.rows[0]
    return {
      id: row.id,
      sampleDate: new Date(row.sample_date),
      sampledAt: new Date(row.sampled_at),
      totalASNsSampled: row.total_asns_sampled,
      ASNsWithASPA: row.asns_with_aspa,
      coveragePercentage: row.coverage_percentage,
      adoptionRateChange: undefined,
      topAdopters: JSON.parse(row.top_adopters || '[]'),
      regions: JSON.parse(row.regions || '[]'),
      sampleMethod: row.sample_method,
      metadata: JSON.parse(row.metadata || '{}'),
    }
  }

  async getStatistics(): Promise<{
    totalSnapshots: number
    daysOfData: number
    averageCoverage: number
    currentCoverage: number
  }> {
    const db = getDatabase()

    const result = await db.query(`
      SELECT
        COUNT(*) as total_snapshots,
        COUNT(DISTINCT DATE(sample_date)) as days_of_data,
        ROUND(AVG(coverage_percentage)::numeric, 2) as average_coverage,
        (SELECT coverage_percentage FROM aspa_adoption_history ORDER BY sample_date DESC LIMIT 1) as current_coverage
      FROM aspa_adoption_history
    `)

    const row = result.rows[0]

    return {
      totalSnapshots: parseInt(row.total_snapshots, 10),
      daysOfData: parseInt(row.days_of_data, 10),
      averageCoverage: parseFloat(row.average_coverage || '0'),
      currentCoverage: parseFloat(row.current_coverage || '0'),
    }
  }
}
