'use client';

import { useCallback, useMemo, useState, useTransition } from 'react';
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts';

import { fetchGenerationMix } from '@/actions/generation.js';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
import {
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
} from '@/components/ui/chart.js';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select.js';
import type { getGenerationMix } from '@/generated/prisma/sql.js';
import { deserialize } from '@/lib/superjson.js';
import { formatMarketDate, formatMarketDateTime, formatMarketTime } from '@/lib/utils.js';

type TimeRange = '24h' | '7d' | '30d' | '90d' | '1y';

const REGIONS = [
  { code: 'PJM', name: 'PJM (Mid-Atlantic)' },
  { code: 'ERCOT', name: 'ERCOT (Texas)' },
  { code: 'CAISO', name: 'CAISO (California)' },
  { code: 'NYISO', name: 'NYISO (New York)' },
  { code: 'ISONE', name: 'ISO-NE (New England)' },
  { code: 'MISO', name: 'MISO (Midwest)' },
  { code: 'SPP', name: 'SPP (South Central)' },
] as const;

const TIME_RANGES: { value: TimeRange; label: string }[] = [
  { value: '24h', label: '24H' },
  { value: '7d', label: '7D' },
  { value: '30d', label: '30D' },
  { value: '90d', label: '90D' },
  { value: '1y', label: '1Y' },
];

const FUEL_TYPES = ['gas', 'nuclear', 'wind', 'solar', 'coal', 'hydro', 'other'] as const;
type FuelType = (typeof FUEL_TYPES)[number];

const FUEL_COLORS: Record<FuelType, string> = {
  gas: 'hsl(25, 95%, 53%)',
  nuclear: 'hsl(270, 70%, 60%)',
  wind: 'hsl(190, 90%, 50%)',
  solar: 'hsl(45, 93%, 58%)',
  coal: 'hsl(0, 0%, 55%)',
  hydro: 'hsl(210, 80%, 55%)',
  other: 'hsl(0, 0%, 40%)',
};

const FUEL_LABELS: Record<FuelType, string> = {
  gas: 'Natural Gas',
  nuclear: 'Nuclear',
  wind: 'Wind',
  solar: 'Solar',
  coal: 'Coal',
  hydro: 'Hydro',
  other: 'Other',
};

const RENEWABLE_FUELS = new Set<FuelType>(['wind', 'solar', 'hydro']);
const FOSSIL_FUELS = new Set<FuelType>(['gas', 'coal']);

const FUEL_TYPE_SET: Set<string> = new Set(FUEL_TYPES);

/** Map EIA fuel type codes to display names */
const EIA_FUEL_MAP: Record<string, FuelType> = {
  NG: 'gas',
  NUC: 'nuclear',
  WND: 'wind',
  SUN: 'solar',
  COL: 'coal',
  WAT: 'hydro',
};

function isFuelType(value: string): value is FuelType {
  return FUEL_TYPE_SET.has(value);
}

function resolveFuelType(raw: string): FuelType {
  return EIA_FUEL_MAP[raw] ?? (isFuelType(raw) ? raw : 'other');
}

const TIME_RANGE_SET: Set<string> = new Set(['24h', '7d', '30d', '90d', '1y']);

function isTimeRange(value: string): value is TimeRange {
  return TIME_RANGE_SET.has(value);
}

const chartConfig: ChartConfig = Object.fromEntries(
  FUEL_TYPES.map(fuel => [fuel, { label: FUEL_LABELS[fuel], color: FUEL_COLORS[fuel] }]),
);

interface PivotedRow {
  timestamp: number;
  dateLabel: string;
  gas: number;
  nuclear: number;
  wind: number;
  solar: number;
  coal: number;
  hydro: number;
  other: number;
}

function pivotGenerationData(rows: getGenerationMix.Result[], regionCode: string): PivotedRow[] {
  const byTimestamp = new Map<number, PivotedRow>();

  for (const row of rows) {
    const ts = row.timestamp.getTime();
    let pivot = byTimestamp.get(ts);
    if (!pivot) {
      pivot = {
        timestamp: ts,
        dateLabel: formatMarketTime(row.timestamp, regionCode),
        gas: 0,
        nuclear: 0,
        wind: 0,
        solar: 0,
        coal: 0,
        hydro: 0,
        other: 0,
      };
      byTimestamp.set(ts, pivot);
    }

    const fuelKey = resolveFuelType(row.fuel_type);
    pivot[fuelKey] += row.generation_mw;
  }

  return Array.from(byTimestamp.values()).sort((a, b) => a.timestamp - b.timestamp);
}

interface GenerationSplit {
  renewable: number;
  fossil: number;
  nuclear: number;
  other: number;
  total: number;
}

function computeGenerationSplit(data: PivotedRow[]): GenerationSplit {
  const totals: GenerationSplit = { renewable: 0, fossil: 0, nuclear: 0, other: 0, total: 0 };

  for (const row of data) {
    for (const fuel of FUEL_TYPES) {
      const mw = row[fuel];
      totals.total += mw;
      if (RENEWABLE_FUELS.has(fuel)) {
        totals.renewable += mw;
      } else if (FOSSIL_FUELS.has(fuel)) {
        totals.fossil += mw;
      } else if (fuel === 'nuclear') {
        totals.nuclear += mw;
      } else {
        totals.other += mw;
      }
    }
  }

  return totals;
}

interface GenerationChartProps {
  initialData: ReturnType<typeof import('@/lib/superjson.js').serialize<getGenerationMix.Result[]>>;
  initialRegion: string;
  initialTimeRange: TimeRange;
}

export function GenerationChart({ initialData, initialRegion, initialTimeRange }: GenerationChartProps) {
  const [regionCode, setRegionCode] = useState(initialRegion);
  const [timeRange, setTimeRange] = useState<TimeRange>(initialTimeRange);
  const [serializedData, setSerializedData] = useState(initialData);
  const [error, setError] = useState<string | null>(null);
  const [isPending, startTransition] = useTransition();

  const rows = useMemo(() => deserialize<getGenerationMix.Result[]>(serializedData), [serializedData]);
  const chartData = useMemo(() => pivotGenerationData(rows, regionCode), [rows, regionCode]);
  const split = useMemo(() => computeGenerationSplit(chartData), [chartData]);

  const refetch = useCallback((newRegion: string, newTimeRange: TimeRange) => {
    startTransition(async () => {
      setError(null);
      const result = await fetchGenerationMix(newRegion, newTimeRange);
      if (result.ok) {
        setSerializedData(result.data);
      } else {
        setError(result.error);
      }
    });
  }, []);

  const handleRegionChange = useCallback(
    (value: string) => {
      setRegionCode(value);
      refetch(value, timeRange);
    },
    [refetch, timeRange],
  );

  const handleTimeRangeChange = useCallback(
    (value: string) => {
      if (!isTimeRange(value)) return;
      setTimeRange(value);
      refetch(regionCode, value);
    },
    [refetch, regionCode],
  );

  const formatPercent = (value: number, total: number) => {
    if (total === 0) return '0%';
    return `${((value / total) * 100).toFixed(1)}%`;
  };

  return (
    <div className="space-y-6">
      <Card>
        <CardHeader>
          <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <CardTitle>Generation by Fuel Type</CardTitle>
              <CardDescription>
                Stacked generation mix over time for {REGIONS.find(r => r.code === regionCode)?.name ?? regionCode}
              </CardDescription>
            </div>
            <div className="flex gap-2">
              <Select value={regionCode} onValueChange={handleRegionChange}>
                <SelectTrigger className="w-[200px]">
                  <SelectValue placeholder="Select region" />
                </SelectTrigger>
                <SelectContent>
                  {REGIONS.map(r => (
                    <SelectItem key={r.code} value={r.code}>
                      {r.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <Select value={timeRange} onValueChange={handleTimeRangeChange}>
                <SelectTrigger className="w-[90px]">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {TIME_RANGES.map(t => (
                    <SelectItem key={t.value} value={t.value}>
                      {t.label}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          </div>
        </CardHeader>
        <CardContent>
          {error && <div className="mb-4 rounded-md bg-destructive/10 p-3 text-sm text-destructive">{error}</div>}

          {chartData.length === 0 && !isPending ? (
            <div className="flex h-80 items-center justify-center rounded-lg border border-dashed border-border">
              <p className="text-sm text-muted-foreground">
                No generation data available for this region and time range.
              </p>
            </div>
          ) : (
            <div className={isPending ? 'opacity-50 transition-opacity' : ''}>
              <ChartContainer config={chartConfig} className="h-80 w-full">
                <AreaChart data={chartData} margin={{ top: 10, right: 10, left: 0, bottom: 0 }}>
                  <defs>
                    {FUEL_TYPES.map(fuel => (
                      <linearGradient key={fuel} id={`fill-${fuel}`} x1="0" y1="0" x2="0" y2="1">
                        <stop offset="5%" stopColor={`var(--color-${fuel})`} stopOpacity={0.8} />
                        <stop offset="95%" stopColor={`var(--color-${fuel})`} stopOpacity={0.1} />
                      </linearGradient>
                    ))}
                  </defs>
                  <CartesianGrid strokeDasharray="3 3" className="stroke-border/50" />
                  <XAxis
                    dataKey="timestamp"
                    type="number"
                    domain={['dataMin', 'dataMax']}
                    scale="time"
                    tickLine={false}
                    axisLine={false}
                    tickMargin={8}
                    minTickGap={40}
                    className="text-xs"
                    tickFormatter={(ts: number) => {
                      const d = new Date(ts);
                      if (timeRange === '24h') return formatMarketTime(d, regionCode);
                      if (timeRange === '7d') return formatMarketDateTime(d, regionCode);
                      return formatMarketDate(d, regionCode);
                    }}
                  />
                  <YAxis
                    tickLine={false}
                    axisLine={false}
                    tickMargin={8}
                    tickFormatter={(value: number) => (value >= 1000 ? `${(value / 1000).toFixed(0)}GW` : `${value}MW`)}
                    className="text-xs"
                  />
                  <ChartTooltip
                    content={
                      <ChartTooltipContent
                        labelFormatter={(_, payload) => {
                          const firstPayload: unknown = payload?.[0]?.payload;
                          if (
                            firstPayload &&
                            typeof firstPayload === 'object' &&
                            'timestamp' in firstPayload &&
                            typeof firstPayload.timestamp === 'number'
                          ) {
                            return formatMarketDateTime(new Date(firstPayload.timestamp), regionCode);
                          }
                          return '';
                        }}
                        formatter={(value, name) => {
                          const mw = typeof value === 'number' ? value : Number(value);
                          const nameStr = String(name);
                          const label = isFuelType(nameStr) ? FUEL_LABELS[nameStr] : nameStr;
                          return (
                            <span>
                              {label}: {mw.toLocaleString(undefined, { maximumFractionDigits: 0 })} MW
                            </span>
                          );
                        }}
                      />
                    }
                  />
                  {FUEL_TYPES.map(fuel => (
                    <Area
                      key={fuel}
                      stackId="generation"
                      type="monotone"
                      dataKey={fuel}
                      fill={`url(#fill-${fuel})`}
                      stroke={`var(--color-${fuel})`}
                      strokeWidth={1.5}
                    />
                  ))}
                  <ChartLegend content={<ChartLegendContent />} />
                </AreaChart>
              </ChartContainer>
            </div>
          )}
        </CardContent>
      </Card>

      {chartData.length > 0 && (
        <div className="grid grid-cols-2 gap-4 sm:grid-cols-4">
          <SplitCard
            label="Renewable"
            value={split.renewable}
            total={split.total}
            formatPercent={formatPercent}
            colorClass="text-cyan-400"
          />
          <SplitCard
            label="Fossil"
            value={split.fossil}
            total={split.total}
            formatPercent={formatPercent}
            colorClass="text-orange-400"
          />
          <SplitCard
            label="Nuclear"
            value={split.nuclear}
            total={split.total}
            formatPercent={formatPercent}
            colorClass="text-purple-400"
          />
          <SplitCard
            label="Other"
            value={split.other}
            total={split.total}
            formatPercent={formatPercent}
            colorClass="text-gray-400"
          />
        </div>
      )}
    </div>
  );
}

function SplitCard({
  label,
  value,
  total,
  formatPercent,
  colorClass,
}: {
  label: string;
  value: number;
  total: number;
  formatPercent: (v: number, t: number) => string;
  colorClass: string;
}) {
  return (
    <Card>
      <CardContent className="pt-6">
        <p className="text-sm text-muted-foreground">{label}</p>
        <p className={`mt-1 text-2xl font-bold tabular-nums ${colorClass}`}>{formatPercent(value, total)}</p>
        <p className="mt-1 text-xs text-muted-foreground">
          {(value / 1000).toLocaleString(undefined, { maximumFractionDigits: 0 })} GWh total
        </p>
      </CardContent>
    </Card>
  );
}
