/**
 * EnergyCharts — self-contained Recharts wrapper for Modbus device trend charts.
 *
 * Design decisions (M5 decision 11):
 * - Recharts is imported ONLY in this file (isolation, easy to swap later).
 * - The component is fully self-contained: it owns its own data fetching via
 *   useReadings / useMetrics hooks and renders loading/error/empty states.
 * - Time-range selection is internal; readings are always fetched with a window
 *   + limit cap — never a full-table pull.
 * - Metric labels and units come from GET /metrics; missing keys in payload are
 *   tolerated (a null data point is emitted so the line simply has a gap).
 */

// ---------------------------------------------------------------------------
// Recharts imports — keep ALL recharts imports inside this file.
// ---------------------------------------------------------------------------

import {
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip as RechartsTooltip,
  Legend,
  ResponsiveContainer,
} from 'recharts'

import { useState, useMemo } from 'react'
import {
  Stack,
  Group,
  Text,
  Loader,
  Alert,
  SegmentedControl,
  Paper,
  Title,
  Badge,
  Box,
} from '@mantine/core'

import { useReadings, useMetrics } from './hooks'
import type { MetricInfo, AutoRefreshOptions } from './hooks'
import { formatMetricValue } from './format'
import { formatLocalTime, formatLocalDateTime } from '../utils/datetime'

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/** Must match the limit passed to useReadings below. */
const CHART_READINGS_LIMIT = 1000

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface EnergyChartsProps {
  /** UUID of the Modbus device to chart. */
  uuid: string
  /** Friendly name for the chart title. */
  deviceName: string
  /** Auto-refresh options forwarded to useReadings. */
  autoRefresh?: AutoRefreshOptions
}

// ---------------------------------------------------------------------------
// Time-range presets
// ---------------------------------------------------------------------------

interface TimePreset {
  label: string
  value: string
  /** How many milliseconds of history to show. */
  spanMs: number
}

const TIME_PRESETS: TimePreset[] = [
  { label: '1 h', value: '1h', spanMs: 60 * 60 * 1000 },
  { label: '6 h', value: '6h', spanMs: 6 * 60 * 60 * 1000 },
  { label: '24 h', value: '24h', spanMs: 24 * 60 * 60 * 1000 },
]

const DEFAULT_PRESET = '1h'

// ---------------------------------------------------------------------------
// Metric colour palette — cycles through a fixed set
// ---------------------------------------------------------------------------

const LINE_COLORS = [
  '#4c9cdb',
  '#f59f00',
  '#51cf66',
  '#f03e3e',
  '#cc5de8',
  '#20c997',
  '#fd7e14',
  '#74c0fc',
]

function lineColor(index: number): string {
  return LINE_COLORS[index % LINE_COLORS.length]
}

// ---------------------------------------------------------------------------
// Helper: format a recorded_at timestamp for the X-axis tick
// ---------------------------------------------------------------------------

function formatTimeTick(isoString: string): string {
  return formatLocalTime(isoString)
}

// ---------------------------------------------------------------------------
// Helper: safe numeric read from payload — returns null if key absent/non-numeric
// ---------------------------------------------------------------------------

function safePayloadValue(
  payload: Record<string, unknown> | null | undefined,
  key: string,
): number | null {
  if (!payload) return null
  const raw = payload[key]
  if (raw === null || raw === undefined) return null
  const n = Number(raw)
  return Number.isFinite(n) ? n : null
}

// ---------------------------------------------------------------------------
// EnergyCharts component
// ---------------------------------------------------------------------------

export function EnergyCharts({ uuid, deviceName, autoRefresh }: EnergyChartsProps) {
  const [activePreset, setActivePreset] = useState<string>(DEFAULT_PRESET)

  const selectedPreset = TIME_PRESETS.find((p) => p.value === activePreset) ?? TIME_PRESETS[0]

  // Pass spanMs rather than fixed start/end so that every refetchInterval-triggered
  // queryFn call computes end=now() and rolls the window forward — new readings
  // recorded after mount will appear without a manual page refresh.
  // Switching presets changes spanMs → different queryKey → immediate re-fetch.
  const readingsQuery = useReadings(
    uuid,
    { spanMs: selectedPreset.spanMs, limit: CHART_READINGS_LIMIT },
    autoRefresh,
  )
  const metricsQuery = useMetrics(uuid)

  // -------------------------------------------------------------------------
  // Derive chart data: [{recorded_at, voltage: 230.2, current: 1.3, ...}, ...]
  // -------------------------------------------------------------------------

  const metricsData = metricsQuery.data?.metrics

  const chartData = useMemo(() => {
    const metrics: MetricInfo[] = metricsData ?? []
    const readings = readingsQuery.data?.items ?? []
    return readings.map((row) => {
      const point: Record<string, string | number | null> = {
        recorded_at: row.recorded_at,
      }
      for (const m of metrics) {
        // Tolerate missing keys — null produces a gap in the line, not a crash.
        point[m.key] = safePayloadValue(row.payload as Record<string, unknown>, m.key)
      }
      return point
    })
  }, [readingsQuery.data, metricsData])

  const metrics: MetricInfo[] = metricsQuery.data?.metrics ?? []

  // -------------------------------------------------------------------------
  // States: loading / error / empty
  // -------------------------------------------------------------------------

  const isLoading = readingsQuery.isLoading || metricsQuery.isLoading
  const isError = readingsQuery.isError || metricsQuery.isError

  /**
   * True when the returned row count equals the limit cap, meaning the window
   * contains more data than was fetched.  The backend returns the most-recent N
   * rows in this case, so the chart shows the latest segment — but a hint is
   * shown so the user knows the full window is not displayed.
   */
  const isTruncated =
    !isLoading &&
    !isError &&
    (readingsQuery.data?.items.length ?? 0) >= CHART_READINGS_LIMIT

  return (
    <Paper withBorder p="md" data-testid={`energy-charts-${uuid}`}>
      <Stack gap="sm">
        {/* Header row */}
        <Group justify="space-between" align="center" wrap="nowrap">
          <Title order={4} data-testid={`energy-charts-title-${uuid}`}>
            {deviceName}
          </Title>
          <SegmentedControl
            size="xs"
            value={activePreset}
            onChange={setActivePreset}
            data={TIME_PRESETS.map((p) => ({ value: p.value, label: p.label }))}
            data-testid={`energy-charts-preset-${uuid}`}
          />
        </Group>

        {/* Loading */}
        {isLoading && (
          <Group justify="center" py="lg" data-testid={`energy-charts-loading-${uuid}`}>
            <Loader size="sm" />
            <Text size="sm" c="dimmed">
              Loading readings…
            </Text>
          </Group>
        )}

        {/* Error */}
        {!isLoading && isError && (
          <Alert color="red" data-testid={`energy-charts-error-${uuid}`}>
            Failed to load readings or metrics. Please try again.
          </Alert>
        )}

        {/* Empty */}
        {!isLoading && !isError && chartData.length === 0 && (
          <Text
            size="sm"
            c="dimmed"
            ta="center"
            py="lg"
            data-testid={`energy-charts-empty-${uuid}`}
          >
            No readings in this time window.
          </Text>
        )}

        {/* Truncation notice — shown when window has more data than the fetch limit */}
        {isTruncated && (
          <Text
            size="xs"
            c="dimmed"
            ta="right"
            data-testid={`energy-charts-truncated-${uuid}`}
          >
            Showing the most recent {CHART_READINGS_LIMIT} readings; full long-range trend pending downsampling
          </Text>
        )}

        {/* Chart */}
        {!isLoading && !isError && chartData.length > 0 && (
          <Box data-testid={`energy-charts-chart-${uuid}`}>
            {/* Render one chart per metric for clarity (avoids mixed Y-axis units) */}
            {metrics.map((metric, idx) => {
              // Check if this metric has any non-null data points; skip if all null.
              const hasData = chartData.some((pt) => pt[metric.key] !== null)
              if (!hasData) return null

              return (
                <Box key={metric.key} mb="md">
                  <Group gap="xs" mb={4}>
                    <Text size="xs" fw={600} c="dimmed">
                      {metric.label}
                    </Text>
                    {metric.unit && (
                      <Badge size="xs" variant="outline" color="gray">
                        {metric.unit}
                      </Badge>
                    )}
                  </Group>
                  <ResponsiveContainer width="100%" height={160}>
                    <LineChart
                      data={chartData}
                      margin={{ top: 4, right: 8, left: 0, bottom: 0 }}
                    >
                      <CartesianGrid strokeDasharray="3 3" opacity={0.4} />
                      <XAxis
                        dataKey="recorded_at"
                        tickFormatter={formatTimeTick}
                        tick={{ fontSize: 10 }}
                        minTickGap={40}
                      />
                      <YAxis
                        tick={{ fontSize: 10 }}
                        width={50}
                        tickFormatter={(v: number) =>
                          metric.unit ? `${v} ${metric.unit}` : String(v)
                        }
                      />
                      <RechartsTooltip
                        formatter={(value) => {
                          const formatted = formatMetricValue(value, metric)
                          if (formatted === '—') return ['—', metric.label] as [string, string]
                          return [
                            `${formatted}${metric.unit ? ' ' + metric.unit : ''}`,
                            metric.label,
                          ] as [string, string]
                        }}
                        labelFormatter={(label) => {
                          return formatLocalDateTime(String(label))
                        }}
                      />
                      <Legend />
                      <Line
                        type="monotone"
                        dataKey={metric.key}
                        name={metric.label}
                        stroke={lineColor(idx)}
                        dot={false}
                        isAnimationActive={false}
                        connectNulls={false}
                      />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )
            })}
          </Box>
        )}
      </Stack>
    </Paper>
  )
}
