import { Router, Request, Response } from 'express'
import { getRepository } from 'typeorm'
import { AssetMaster } from '../entities/AssetMaster'
import { AssetMonitoringData } from '../entities/AssetMonitoringData'
import { Meter } from '../entities/Meter'
import { ParameterMaster } from '../entities/ParameterMaster'
import { authMiddleware } from '../middleware/auth'

const router = Router()

// Apply auth middleware to all routes
router.use(authMiddleware)

/**
 * GET /api/realtime/summary
 * Get KPI summary (active assets, data rate, system health)
 */
router.get('/summary', async (req: Request, res: Response) => {
  try {
    const assetRepo = getRepository(AssetMaster)
    const monitoringRepo = getRepository(AssetMonitoringData)

    // Count active assets
    const activeAssets = await assetRepo.count({
      where: [
        { iot_device_mapped: 1, is_running: 1 }
      ]
    })

    // Count total readings in last 24h
    const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000)
    const totalReadings = await monitoringRepo
      .createQueryBuilder('amd')
      .where('amd.recorded_at > :time', { time: twentyFourHoursAgo })
      .getCount()

    // Count readings in last hour for data rate
    const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000)
    const recentReadings = await monitoringRepo
      .createQueryBuilder('amd')
      .where('amd.recorded_at > :time', { time: oneHourAgo })
      .getCount()

    // Calculate system health (% of assets reporting in last 5 minutes)
    const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)
    const reportingAssets = await monitoringRepo
      .createQueryBuilder('amd')
      .select('COUNT(DISTINCT amd.asset_id)', 'count')
      .where('amd.recorded_at > :time', { time: fiveMinutesAgo })
      .getRawOne()

    const healthPercent = activeAssets > 0
      ? Math.round(((reportingAssets?.count || 0) / activeAssets) * 100)
      : 0

    // Count critical alerts (assets with criticality = 'critical' or status issues)
    const criticalAlerts = await assetRepo.count({
      where: { criticality: 'critical', is_running: 1 }
    })

    // Count stale sources (not reported in 5+ minutes)
    const staleSources = await monitoringRepo
      .createQueryBuilder('amd')
      .select('COUNT(DISTINCT amd.asset_id)', 'count')
      .where('amd.recorded_at < :time', { time: fiveMinutesAgo })
      .getRawOne()

    res.json({
      active_assets: activeAssets,
      total_data_points: totalReadings,
      data_rate_points_per_min: Math.floor(recentReadings / 60),
      system_health_percent: healthPercent,
      critical_alerts_count: criticalAlerts,
      stale_sources_count: staleSources?.count || 0
    })
  } catch (error) {
    console.error('Error fetching realtime summary:', error)
    res.status(500).json({ error: 'Failed to fetch summary' })
  }
})

/**
 * GET /api/realtime/streams
 * Get all live data streams with latency and freshness
 */
router.get('/streams', async (req: Request, res: Response) => {
  try {
    const monitoringRepo = getRepository(AssetMonitoringData)
    const assetRepo = getRepository(AssetMaster)

    const streams = await assetRepo
      .createQueryBuilder('am')
      .leftJoinAndSelect('am.monitoring_data', 'amd')
      .select('am.asset_id', 'asset_id')
      .addSelect('am.device_id', 'device_id')
      .addSelect('am.device_reference_id', 'device_reference_id')
      .addSelect('COUNT(amd.id)', 'total_readings')
      .addSelect('MAX(amd.recorded_at)', 'last_reading')
      .where('am.iot_device_mapped = :mapped', { mapped: 1 })
      .groupBy('am.asset_id, am.device_id, am.device_reference_id')
      .orderBy('last_reading', 'DESC')
      .getRawMany()

    // Transform and calculate latency/freshness
    const result = streams.map((stream: any) => {
      const lastReadingTime = new Date(stream.last_reading).getTime()
      const now = Date.now()
      const latencySeconds = (now - lastReadingTime) / 1000
      const freshness = Math.max(0, 100 - (latencySeconds / 300) * 100)

      return {
        asset_id: stream.asset_id,
        device_id: stream.device_id,
        device_reference_id: stream.device_reference_id,
        total_readings: parseInt(stream.total_readings) || 0,
        last_reading: stream.last_reading,
        latency_seconds: Math.round(latencySeconds),
        freshness_percent: Math.round(freshness),
        status:
          freshness > 80 ? 'healthy' :
          freshness > 50 ? 'warning' :
          'offline'
      }
    })

    res.json(result)
  } catch (error) {
    console.error('Error fetching live streams:', error)
    res.status(500).json({ error: 'Failed to fetch streams' })
  }
})

/**
 * GET /api/realtime/sensors/current
 * Get current sensor readings
 */
router.get('/sensors/current', async (req: Request, res: Response) => {
  try {
    const monitoringRepo = getRepository(AssetMonitoringData)

    const readings = await monitoringRepo
      .createQueryBuilder('amd')
      .leftJoinAndSelect('amd.asset', 'am')
      .leftJoinAndSelect('amd.parameter', 'pm')
      .orderBy('amd.recorded_at', 'DESC')
      .take(1000)
      .getMany()

    const result = readings.map((reading) => ({
      asset_id: reading.asset_id,
      serialnumber: reading.asset?.serialnumber,
      device_id: reading.asset?.device_id,
      parameter_name: reading.parameter?.parameter_name || reading.tag_code,
      current_value: Number(reading.value_num) || null,
      unit: reading.unit,
      last_reading_time: reading.recorded_at,
      criticality: reading.asset?.criticality,
      is_running: reading.asset?.is_running,
      age_seconds: Math.round((Date.now() - reading.recorded_at.getTime()) / 1000)
    }))

    res.json(result)
  } catch (error) {
    console.error('Error fetching sensor readings:', error)
    res.status(500).json({ error: 'Failed to fetch sensor readings' })
  }
})

/**
 * GET /api/realtime/sensors/stats
 * Get sensor type statistics (count by type, % reporting)
 */
router.get('/sensors/stats', async (req: Request, res: Response) => {
  try {
    const paramRepo = getRepository(ParameterMaster)
    const monitoringRepo = getRepository(AssetMonitoringData)
    const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)

    const stats = await paramRepo
      .createQueryBuilder('pm')
      .select('pm.parameter_name', 'parameter_name')
      .addSelect('COUNT(DISTINCT amd.asset_id)', 'sensor_count')
      .leftJoin(AssetMonitoringData, 'amd', 'amd.param_id = pm.param_id')
      .addSelect(
        `ROUND(COUNT(CASE WHEN amd.recorded_at > :recentTime THEN amd.asset_id END)::numeric /
                NULLIF(COUNT(DISTINCT amd.asset_id), 0) * 100, 2)`,
        'reporting_percent'
      )
      .where('pm.parameter_name IS NOT NULL')
      .setParameters({ recentTime: fiveMinutesAgo })
      .groupBy('pm.parameter_name')
      .orderBy('sensor_count', 'DESC')
      .getRawMany()

    const result = stats.map((stat: any) => ({
      parameter_name: stat.parameter_name,
      sensor_count: parseInt(stat.sensor_count) || 0,
      reporting_percent: parseFloat(stat.reporting_percent) || 0
    }))

    res.json(result)
  } catch (error) {
    console.error('Error fetching sensor stats:', error)
    res.status(500).json({ error: 'Failed to fetch sensor stats' })
  }
})

/**
 * GET /api/realtime/meters
 * Get meter consumption data
 */
router.get('/meters', async (req: Request, res: Response) => {
  try {
    const meterRepo = getRepository(Meter)

    const meters = await meterRepo
      .createQueryBuilder('m')
      .leftJoinAndSelect('m.asset', 'am')
      .orderBy('m.last_reading_date', 'DESC')
      .getMany()

    const result = meters.map((meter) => ({
      meter_id: meter.meter_id,
      meter_name: meter.meter_name,
      meter_type: meter.meter_type,
      current_flow_lps: Number(meter.current_flow_lps) || 0,
      daily_volume_m3: Number(meter.daily_volume_m3) || 0,
      total_volume_m3: Number(meter.total_volume_m3) || 0,
      last_reading_date: meter.last_reading_date,
      status:
        meter.last_reading_date > new Date(Date.now() - 24 * 60 * 60 * 1000) ? 'active' :
        meter.last_reading_date > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) ? 'warning' :
        'offline',
      days_since_reading: meter.last_reading_date
        ? Math.floor((Date.now() - meter.last_reading_date.getTime()) / (24 * 60 * 60 * 1000))
        : null
    }))

    res.json(result)
  } catch (error) {
    console.error('Error fetching meter data:', error)
    res.status(500).json({ error: 'Failed to fetch meter data' })
  }
})

/**
 * GET /api/realtime/devices/registry
 * Get device/asset registry with live status
 */
router.get('/devices/registry', async (req: Request, res: Response) => {
  try {
    const assetRepo = getRepository(AssetMaster)

    const devices = await assetRepo
      .createQueryBuilder('am')
      .where('am.iot_device_mapped = :mapped', { mapped: 1 })
      .orderBy('am.last_tracking_time', 'DESC')
      .getMany()

    const result = devices.map((device) => ({
      asset_id: device.asset_id,
      ic3_global_id: device.ic3_global_id,
      serialnumber: device.serialnumber,
      device_id: device.device_id,
      device_reference_id: device.device_reference_id,
      is_running: device.is_running,
      criticality: device.criticality,
      last_tracking_time: device.last_tracking_time,
      age_seconds: device.last_tracking_time
        ? Math.round((Date.now() - device.last_tracking_time.getTime()) / 1000)
        : null,
      status: device.is_running === 1 ? 'online' : 'offline'
    }))

    res.json(result)
  } catch (error) {
    console.error('Error fetching device registry:', error)
    res.status(500).json({ error: 'Failed to fetch device registry' })
  }
})

/**
 * GET /api/realtime/sources/stale
 * Get stale sources (not reporting in 5+ minutes)
 */
router.get('/sources/stale', async (req: Request, res: Response) => {
  try {
    const monitoringRepo = getRepository(AssetMonitoringData)
    const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)

    const staleSources = await monitoringRepo
      .createQueryBuilder('amd')
      .leftJoinAndSelect('amd.asset', 'am')
      .select('am.asset_id', 'asset_id')
      .addSelect('am.device_id', 'device_id')
      .addSelect('am.device_reference_id', 'device_reference_id')
      .addSelect('MAX(amd.recorded_at)', 'last_reading')
      .addSelect('COUNT(*)', 'total_readings_ever')
      .where('amd.recorded_at < :time', { time: fiveMinutesAgo })
      .groupBy('am.asset_id, am.device_id, am.device_reference_id')
      .orderBy('last_reading', 'ASC')
      .getRawMany()

    const result = staleSources.map((source: any) => ({
      asset_id: source.asset_id,
      device_id: source.device_id,
      device_reference_id: source.device_reference_id,
      last_reading: source.last_reading,
      minutes_ago: Math.round((Date.now() - new Date(source.last_reading).getTime()) / 60000),
      total_readings_ever: parseInt(source.total_readings_ever) || 0
    }))

    res.json(result)
  } catch (error) {
    console.error('Error fetching stale sources:', error)
    res.status(500).json({ error: 'Failed to fetch stale sources' })
  }
})

/**
 * GET /api/realtime/alerts
 * Get critical alerts
 */
router.get('/alerts', async (req: Request, res: Response) => {
  try {
    const assetRepo = getRepository(AssetMaster)

    const criticalAssets = await assetRepo
      .createQueryBuilder('am')
      .where('am.criticality = :crit OR am.asset_live_status = :offline', {
        crit: 'critical',
        offline: 0
      })
      .orderBy('am.last_tracking_time', 'DESC')
      .take(10)
      .getMany()

    const result = criticalAssets.map((asset) => ({
      asset_id: asset.asset_id,
      device_id: asset.device_id,
      criticality: asset.criticality,
      status: asset.is_running === 1 ? 'online' : 'offline',
      message: `${asset.device_id}: ${asset.criticality === 'critical' ? 'Critical asset' : 'Offline'} - Last seen ${Math.round((Date.now() - asset.last_tracking_time.getTime()) / 60000)} min ago`,
      timestamp: asset.last_tracking_time,
      severity: asset.criticality === 'critical' ? 'critical' : 'warning'
    }))

    res.json(result)
  } catch (error) {
    console.error('Error fetching alerts:', error)
    res.status(500).json({ error: 'Failed to fetch alerts' })
  }
})

/**
 * GET /api/realtime/dashboard
 * Get complete realtime dashboard data (all in one call)
 */
router.get('/dashboard', async (req: Request, res: Response) => {
  try {
    // Fetch all data in parallel
    const [summary, streams, sensors, sensorStats, meters, devices, staleSources, alerts] =
      await Promise.all([
        fetch(`${req.originalUrl.split('/dashboard')[0]}/summary`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/streams`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/sensors/current`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/sensors/stats`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/meters`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/devices/registry`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/sources/stale`).then(r => r.json()),
        fetch(`${req.originalUrl.split('/dashboard')[0]}/alerts`).then(r => r.json())
      ])

    res.json({
      summary: summary || {},
      data_streams: streams || [],
      sensor_readings: sensors || [],
      sensor_stats: sensorStats || [],
      meters: meters || [],
      alerts: alerts || [],
      stale_sources: staleSources || [],
      latency_analysis: []
    })
  } catch (error) {
    console.error('Error fetching dashboard:', error)
    res.status(500).json({ error: 'Failed to fetch dashboard data' })
  }
})

export default router
