import type { ReactNode } from 'react';
import { Card, Group, SimpleGrid, Text } from '@mantine/core';
import {
  Area,
  AreaChart,
  Bar,
  BarChart,
  CartesianGrid,
  Cell,
  Legend,
  Line,
  LineChart,
  Pie,
  PieChart,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from 'recharts';
import type { BreakdownItem, ReportGranularity, VesselReport } from '@ema-platform/api';
import {
  expiryBands,
  formatBucket,
  formatNumber,
  officerLabel,
  sliceColor,
} from './report-format';

// Recharts is unused elsewhere in this repo, so the shared setup lives here
// rather than being repeated per chart: one grid style, one tooltip style, one
// axis style, and a fixed height so the dashboard's rows line up.
const CHART_HEIGHT = 260;
const AXIS = { fontSize: 11, stroke: 'var(--mantine-color-dimmed)' } as const;
const GRID = 'var(--mantine-color-default-border)';

const TOOLTIP_STYLE = {
  background: 'var(--mantine-color-body)',
  border: '1px solid var(--mantine-color-default-border)',
  borderRadius: 8,
  fontSize: 12,
} as const;

function ChartCard({
  title,
  subtitle,
  children,
  empty,
}: {
  title: string;
  subtitle?: string;
  children: ReactNode;
  /** True when there is genuinely nothing to draw — say so, don't draw axes. */
  empty?: boolean;
}) {
  return (
    <Card withBorder radius="md" p="md">
      <Group justify="space-between" mb="xs" wrap="nowrap">
        <Text fw={600} size="sm">
          {title}
        </Text>
        {subtitle && (
          <Text size="xs" c="dimmed">
            {subtitle}
          </Text>
        )}
      </Group>
      {empty ? (
        <Text size="sm" c="dimmed" py="xl" ta="center">
          Nothing to show for this filter yet.
        </Text>
      ) : (
        <ResponsiveContainer width="100%" height={CHART_HEIGHT}>
          {children as never}
        </ResponsiveContainer>
      )}
    </Card>
  );
}

/**
 * A ranked breakdown as horizontal bars.
 *
 * Horizontal because the labels are flag states, ports and vessel types —
 * words, which a vertical axis can show in full instead of rotating them.
 */
function BreakdownBars({
  title,
  subtitle,
  items,
}: {
  title: string;
  subtitle?: string;
  items: BreakdownItem[];
  }) {
  return (
    <ChartCard title={title} subtitle={subtitle} empty={items.length === 0}>
      <BarChart data={items} layout="vertical" margin={{ left: 8, right: 16 }}>
        <CartesianGrid strokeDasharray="3 3" stroke={GRID} horizontal={false} />
        <XAxis type="number" allowDecimals={false} {...AXIS} />
        <YAxis type="category" dataKey="label" width={130} {...AXIS} />
        <Tooltip
          contentStyle={TOOLTIP_STYLE}
          formatter={(value, _name, entry) => [
            countWithShare(value, entry),
            'Vessels',
          ]}
        />
        <Bar dataKey="count" radius={[0, 4, 4, 0]}>
          {items.map((item, index) => (
            <Cell key={item.key} fill={sliceColor(item, index)} />
          ))}
        </Bar>
      </BarChart>
    </ChartCard>
  );
}

function BreakdownDonut({
  title,
  subtitle,
  items,
}: {
  title: string;
  subtitle?: string;
  items: BreakdownItem[];
}) {
  return (
    <ChartCard title={title} subtitle={subtitle} empty={items.length === 0}>
      <PieChart>
        <Pie
          data={items}
          dataKey="count"
          nameKey="label"
          innerRadius="52%"
          outerRadius="78%"
          paddingAngle={2}
        >
          {items.map((item, index) => (
            <Cell key={item.key} fill={sliceColor(item, index)} />
          ))}
        </Pie>
        <Tooltip
          contentStyle={TOOLTIP_STYLE}
          formatter={(value, name, entry) => [countWithShare(value, entry), name]}
        />
        <Legend
          verticalAlign="bottom"
          height={36}
          wrapperStyle={{ fontSize: 11 }}
        />
      </PieChart>
    </ChartCard>
  );
}

/**
 * "12 (7.5%)" for a breakdown tooltip.
 *
 * The share comes off the payload rather than being recomputed: the API's
 * percentage is of the whole, including the slices folded into "Other", and
 * dividing by what is on screen would quietly disagree with it.
 */
function countWithShare(value: unknown, entry: unknown): string {
  const count = typeof value === 'number' ? value : Number(value ?? 0);
  const payload = (entry as { payload?: BreakdownItem } | undefined)?.payload;
  const share = payload?.percentage ?? 0;
  return `${formatNumber(count)} (${formatNumber(share, { decimals: 1 })}%)`;
}

/** True when every bucket in a zero-filled series is empty. */
const allZero = (values: number[]): boolean =>
  values.every((value) => value === 0);

export function ReportCharts({ report }: { report: VesselReport }) {
  const { timeSeries, breakdowns, kpis } = report;
  const granularity: ReportGranularity = report.filters.granularity;
  const tick = (bucket: string) => formatBucket(bucket, granularity);
  // Recharts types the tooltip label as a ReactNode; only a string is ever a
  // bucket key, and anything else is passed through untouched.
  const tickLabel = (label: unknown) =>
    typeof label === 'string' ? tick(label) : String(label ?? '');

  // Expiry counts arrive cumulative; drawn side by side they have to be
  // disjoint or the three bars double-count each other.
  const expiry = expiryBands(kpis.certificates);

  const officers = breakdowns.byOfficer.map((item) => ({
    ...item,
    label: officerLabel(item.key),
  }));

  return (
    <>
      <SimpleGrid cols={{ base: 1, lg: 2 }} spacing="md">
        <ChartCard
          title="Registrations over time"
          subtitle="count and gross tonnage"
          empty={allZero(timeSeries.registrations.map((b) => b.count))}
        >
          <AreaChart data={timeSeries.registrations}>
            <CartesianGrid strokeDasharray="3 3" stroke={GRID} />
            <XAxis dataKey="bucket" tickFormatter={tick} {...AXIS} />
            <YAxis yAxisId="count" allowDecimals={false} {...AXIS} />
            <YAxis yAxisId="tonnage" orientation="right" {...AXIS} />
            <Tooltip contentStyle={TOOLTIP_STYLE} labelFormatter={tickLabel} />
            <Legend wrapperStyle={{ fontSize: 11 }} />
            <Area
              yAxisId="count"
              type="monotone"
              dataKey="count"
              name="Vessels"
              stroke="var(--mantine-color-blue-6)"
              fill="var(--mantine-color-blue-2)"
            />
            <Area
              yAxisId="tonnage"
              type="monotone"
              dataKey="grossTonnage"
              name="Gross tonnage"
              stroke="var(--mantine-color-teal-6)"
              fill="transparent"
            />
          </AreaChart>
        </ChartCard>

        <ChartCard
          title="Application throughput"
          subtitle="decisions land in the month they were made"
          empty={allZero(
            timeSeries.applications.flatMap((b) => [
              b.submitted,
              b.approved,
              b.rejected,
            ]),
          )}
        >
          <BarChart data={timeSeries.applications}>
            <CartesianGrid strokeDasharray="3 3" stroke={GRID} />
            <XAxis dataKey="bucket" tickFormatter={tick} {...AXIS} />
            <YAxis allowDecimals={false} {...AXIS} />
            <Tooltip contentStyle={TOOLTIP_STYLE} labelFormatter={tickLabel} />
            <Legend wrapperStyle={{ fontSize: 11 }} />
            <Bar
              dataKey="submitted"
              name="Submitted"
              fill="var(--mantine-color-blue-4)"
            />
            {/* Approved and rejected stack: together they are the decisions
                made in that bucket, which reads against intake beside it. */}
            <Bar
              dataKey="approved"
              name="Approved"
              stackId="decided"
              fill="var(--mantine-color-teal-6)"
            />
            <Bar
              dataKey="rejected"
              name="Rejected"
              stackId="decided"
              fill="var(--mantine-color-red-6)"
            />
          </BarChart>
        </ChartCard>

        <ChartCard
          title="Fees collected"
          subtitle={kpis.revenue.currency}
          empty={allZero(timeSeries.revenue.map((b) => b.amount))}
        >
          <LineChart data={timeSeries.revenue}>
            <CartesianGrid strokeDasharray="3 3" stroke={GRID} />
            <XAxis dataKey="bucket" tickFormatter={tick} {...AXIS} />
            <YAxis {...AXIS} />
            <Tooltip contentStyle={TOOLTIP_STYLE} labelFormatter={tickLabel} />
            <Line
              type="monotone"
              dataKey="amount"
              name={`Paid (${kpis.revenue.currency})`}
              stroke="var(--mantine-color-yellow-7)"
              strokeWidth={2}
              dot={false}
            />
          </LineChart>
        </ChartCard>

        <ChartCard
          title="Incidents over time"
          empty={allZero(timeSeries.incidents.map((b) => b.count))}
        >
          <BarChart data={timeSeries.incidents}>
            <CartesianGrid strokeDasharray="3 3" stroke={GRID} />
            <XAxis dataKey="bucket" tickFormatter={tick} {...AXIS} />
            <YAxis allowDecimals={false} {...AXIS} />
            <Tooltip contentStyle={TOOLTIP_STYLE} labelFormatter={tickLabel} />
            <Bar
              dataKey="count"
              name="Incidents"
              fill="var(--mantine-color-orange-6)"
              radius={[4, 4, 0, 0]}
            />
          </BarChart>
        </ChartCard>
      </SimpleGrid>

      <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md" mt="md">
        <BreakdownDonut title="Register status" items={breakdowns.byStatus} />
        <BreakdownDonut title="Category" items={breakdowns.byCategory} />
        <ChartCard
          title="Certificate expiry"
          subtitle="disjoint bands"
          empty={allZero(expiry.map((band) => band.count))}
        >
          <BarChart data={expiry} layout="vertical" margin={{ left: 8, right: 16 }}>
            <CartesianGrid strokeDasharray="3 3" stroke={GRID} horizontal={false} />
            <XAxis type="number" allowDecimals={false} {...AXIS} />
            <YAxis type="category" dataKey="label" width={110} {...AXIS} />
            <Tooltip contentStyle={TOOLTIP_STYLE} />
            <Bar
              dataKey="count"
              name="Certificates"
              fill="var(--mantine-color-orange-6)"
              radius={[0, 4, 4, 0]}
            />
          </BarChart>
        </ChartCard>

        <BreakdownBars title="Tonnage bands" items={breakdowns.byTonnageBand} />
        <BreakdownBars title="Age bands" items={breakdowns.byAgeBand} />
        <BreakdownBars title="Length bands" items={breakdowns.byLengthBand} />

        <BreakdownBars
          title="Flag states"
          subtitle="top slices, rest grouped"
          items={breakdowns.byFlagState}
        />
        <BreakdownBars
          title="Ports of registry"
          subtitle="top slices, rest grouped"
          items={breakdowns.byPortOfRegistry}
        />
        <BreakdownBars title="Vessel types" items={breakdowns.byVesselType} />

        <BreakdownBars title="Hull material" items={breakdowns.byHullMaterial} />
        <BreakdownBars title="Engine type" items={breakdowns.byEngineType} />
        <BreakdownBars title="Build decade" items={breakdowns.byBuildDecade} />

        <BreakdownBars
          title="Application status"
          items={breakdowns.byApplicationStatus}
        />
        <BreakdownDonut
          title="New vs renewal"
          items={breakdowns.byApplicationKind}
        />
        <BreakdownDonut
          title="Incident severity"
          subtitle="free text on the register"
          items={breakdowns.byIncidentSeverity}
        />

        <BreakdownBars
          title="Officer workload"
          subtitle="user id — names not resolved"
          items={officers}
        />
      </SimpleGrid>
    </>
  );
}
