'use client';

import { useMemo } from 'react';
import { CartesianGrid, ComposedChart, Label, Line, Scatter, XAxis, YAxis, ZAxis } from 'recharts';
import type { SuperJSONResult } from 'superjson';

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from '@/components/ui/chart.js';
import { deserialize } from '@/lib/superjson.js';

interface RegionCapacityPrice {
  region_code: string;
  avg_price: number;
  total_capacity_mw: number;
}

interface CorrelationChartProps {
  data: SuperJSONResult;
}

const REGION_COLORS: Record<string, string> = {
  PJM: 'hsl(210, 90%, 55%)',
  ERCOT: 'hsl(25, 90%, 55%)',
  CAISO: 'hsl(140, 70%, 45%)',
  NYISO: 'hsl(280, 70%, 55%)',
  ISONE: 'hsl(340, 70%, 55%)',
  MISO: 'hsl(55, 80%, 50%)',
  SPP: 'hsl(180, 60%, 45%)',
  BPA: 'hsl(95, 55%, 50%)',
  NWMT: 'hsl(310, 50%, 55%)',
  WAPA: 'hsl(165, 50%, 50%)',
  TVA: 'hsl(15, 70%, 50%)',
  DUKE: 'hsl(240, 55%, 60%)',
  SOCO: 'hsl(350, 55%, 50%)',
  FPC: 'hsl(45, 60%, 45%)',
};

const chartConfig: ChartConfig = {
  correlation: {
    label: 'DC Capacity vs. Avg Price',
    color: 'hsl(210, 90%, 55%)',
  },
};

interface ScatterPoint {
  region_code: string;
  total_capacity_mw: number;
  avg_price: number;
  fill: string;
  /** Dot radius scales with capacity — used as ZAxis value */
  z: number;
}

interface TrendLinePoint {
  total_capacity_mw: number;
  trendPrice: number;
}

interface RegressionResult {
  slope: number;
  intercept: number;
  r2: number;
  line: TrendLinePoint[];
}

/** Simple OLS linear regression: y = slope * x + intercept */
function linearRegression(points: ScatterPoint[]): RegressionResult | null {
  const n = points.length;
  if (n < 2) return null;

  let sumX = 0;
  let sumY = 0;
  let sumXY = 0;
  let sumX2 = 0;

  for (const p of points) {
    sumX += p.total_capacity_mw;
    sumY += p.avg_price;
    sumXY += p.total_capacity_mw * p.avg_price;
    sumX2 += p.total_capacity_mw * p.total_capacity_mw;
  }

  const denom = n * sumX2 - sumX * sumX;
  if (denom === 0) return null;

  const slope = (n * sumXY - sumX * sumY) / denom;
  const intercept = (sumY - slope * sumX) / n;

  // Pearson R squared
  const ssRes = points.reduce((s, p) => {
    const predicted = slope * p.total_capacity_mw + intercept;
    return s + (p.avg_price - predicted) ** 2;
  }, 0);
  const meanY = sumY / n;
  const ssTot = points.reduce((s, p) => s + (p.avg_price - meanY) ** 2, 0);
  const r2 = ssTot === 0 ? 0 : 1 - ssRes / ssTot;

  // Build line points from min to max X
  const xs = points.map(p => p.total_capacity_mw);
  const minX = Math.min(...xs);
  const maxX = Math.max(...xs);
  const line: TrendLinePoint[] = [
    { total_capacity_mw: minX, trendPrice: slope * minX + intercept },
    { total_capacity_mw: maxX, trendPrice: slope * maxX + intercept },
  ];

  return { slope, intercept, r2, line };
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null;
}

function getNumberProp(obj: Record<string, unknown>, key: string): number {
  const val = obj[key];
  return typeof val === 'number' ? val : 0;
}

function getScatterPayload(obj: Record<string, unknown>): ScatterPoint | null {
  const payload = obj['payload'];
  if (!isRecord(payload)) return null;
  const regionCode = typeof payload['region_code'] === 'string' ? payload['region_code'] : '';
  const fill = typeof payload['fill'] === 'string' ? payload['fill'] : 'hsl(0,0%,50%)';
  return {
    region_code: regionCode,
    total_capacity_mw: getNumberProp(payload, 'total_capacity_mw'),
    avg_price: getNumberProp(payload, 'avg_price'),
    fill,
    z: getNumberProp(payload, 'z'),
  };
}

function CustomDot(props: unknown): React.JSX.Element {
  if (!isRecord(props)) return <g />;
  const cx = getNumberProp(props, 'cx');
  const cy = getNumberProp(props, 'cy');
  const payload = getScatterPayload(props);
  if (!payload) return <g />;

  // Radius derived from ZAxis mapping — use z value to compute a proportional radius
  const radius = Math.max(6, Math.min(20, payload.z / 80));

  return (
    <g>
      <circle cx={cx} cy={cy} r={radius} fill={payload.fill} fillOpacity={0.6} stroke={payload.fill} strokeWidth={2} />
      <text
        x={cx}
        y={cy - radius - 6}
        textAnchor="middle"
        fill="var(--color-foreground)"
        fontSize={11}
        fontWeight={600}>
        {payload.region_code}
      </text>
    </g>
  );
}

export function CorrelationChart({ data }: CorrelationChartProps) {
  const rows = useMemo(() => deserialize<RegionCapacityPrice[]>(data), [data]);

  const scatterData: ScatterPoint[] = useMemo(
    () =>
      rows
        .filter(r => r.total_capacity_mw > 0 || r.avg_price > 0)
        .map(r => ({
          region_code: r.region_code,
          total_capacity_mw: Math.round(r.total_capacity_mw),
          avg_price: Number(r.avg_price.toFixed(2)),
          fill: REGION_COLORS[r.region_code] ?? 'hsl(0, 0%, 50%)',
          z: Math.round(r.total_capacity_mw),
        })),
    [rows],
  );

  const regression = useMemo(() => linearRegression(scatterData), [scatterData]);

  // Merge scatter + trend line data for ComposedChart
  const combinedData = useMemo(() => {
    const scattered = scatterData.map(p => ({
      ...p,
      trendPrice: undefined as number | undefined,
    }));
    if (!regression) return scattered;
    // Add two trend-line-only points
    const trendPoints = regression.line.map(t => ({
      region_code: '',
      avg_price: undefined as number | undefined,
      total_capacity_mw: t.total_capacity_mw,
      fill: '',
      z: 0,
      trendPrice: Number(t.trendPrice.toFixed(2)),
    }));
    return [...scattered, ...trendPoints].sort((a, b) => a.total_capacity_mw - b.total_capacity_mw);
  }, [scatterData, regression]);

  if (scatterData.length === 0) {
    return (
      <Card>
        <CardHeader>
          <CardTitle>DC Capacity vs. Electricity Price</CardTitle>
          <CardDescription>No data available for correlation analysis.</CardDescription>
        </CardHeader>
        <CardContent>
          <div className="flex h-64 items-center justify-center rounded-lg border border-dashed border-border">
            <p className="text-sm text-muted-foreground">
              No data available. Ingest price data and seed datacenters first.
            </p>
          </div>
        </CardContent>
      </Card>
    );
  }

  return (
    <Card>
      <CardHeader>
        <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <CardTitle>DC Capacity vs. Electricity Price</CardTitle>
            <CardDescription>Datacenter capacity (MW) versus average electricity price per region</CardDescription>
          </div>
          {regression && (
            <div className="flex items-center gap-3 rounded-lg border border-border bg-muted/50 px-3 py-2">
              <div className="text-xs text-muted-foreground">
                <span className="font-medium text-foreground">R² = {regression.r2.toFixed(3)}</span>
                <span className="ml-2">
                  {regression.r2 >= 0.7 ? 'Strong' : regression.r2 >= 0.4 ? 'Moderate' : 'Weak'} correlation
                </span>
              </div>
              <div className="h-3 w-px bg-border" />
              <div className="flex items-center gap-1.5 text-xs text-muted-foreground">
                <span className="inline-block h-0.5 w-4 bg-foreground/40" />
                Trend line
              </div>
            </div>
          )}
        </div>
      </CardHeader>
      <CardContent>
        <ChartContainer config={chartConfig} className="h-[40vh] max-h-125 min-h-75 w-full">
          <ComposedChart data={combinedData} margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>
            <CartesianGrid strokeDasharray="3 3" className="stroke-border/30" />
            <XAxis
              type="number"
              dataKey="total_capacity_mw"
              name="DC Capacity"
              tick={{ fontSize: 11, fill: 'var(--color-muted-foreground)' }}
              tickLine={false}
              axisLine={false}
              tickFormatter={(v: number) => `${v} MW`}>
              <Label
                value="Total DC Capacity (MW)"
                offset={-10}
                position="insideBottom"
                style={{ fontSize: 11, fill: 'var(--color-muted-foreground)' }}
              />
            </XAxis>
            <YAxis
              type="number"
              dataKey="avg_price"
              name="Avg Price"
              tick={{ fontSize: 11, fill: 'var(--color-muted-foreground)' }}
              tickLine={false}
              axisLine={false}
              tickFormatter={(v: number) => `$${v}`}
              allowDataOverflow>
              <Label
                value="Avg Price ($/MWh)"
                angle={-90}
                position="insideLeft"
                style={{ fontSize: 11, fill: 'var(--color-muted-foreground)' }}
              />
            </YAxis>
            <ZAxis dataKey="z" range={[100, 1600]} />
            <ChartTooltip
              content={
                <ChartTooltipContent
                  formatter={(value, name) => {
                    const nameStr = String(name);
                    const valStr = String(value);
                    if (nameStr === 'DC Capacity') return [`${valStr} MW`, undefined];
                    if (nameStr === 'Avg Price') return [`$${valStr}/MWh`, undefined];
                    return [valStr, undefined];
                  }}
                />
              }
            />
            {/* Trend line */}
            {regression && (
              <Line
                type="linear"
                dataKey="trendPrice"
                stroke="color-mix(in oklch, var(--color-foreground) 30%, transparent)"
                strokeWidth={2}
                strokeDasharray="8 4"
                dot={false}
                connectNulls
                isAnimationActive={false}
              />
            )}
            <Scatter name="Regions" dataKey="avg_price" data={scatterData} shape={CustomDot} />
          </ComposedChart>
        </ChartContainer>
      </CardContent>
    </Card>
  );
}
