import { useMemo } from 'react';
import { Text, View } from 'react-native';
import Svg, { Path, Circle } from 'react-native-svg';
import { useTranslation } from 'react-i18next';
import { useColors } from '../../lib/theme';
import type { BlockedByConnectionEntry } from '../../hooks/useMailStats';
type Props = {
data: BlockedByConnectionEntry[];
/** When true: renders as full-width hero card with integrated title row */
hero?: boolean;
totalBlocked?: number;
accountCount?: number;
isLegend?: boolean;
};
const SLICE_COLORS = ['#ef4444', '#3b82f6', '#f59e0b', '#8b5cf6'];
const OTHER_COLOR = '#a3a3a3';
// Legend cap: show max 3 named entries + optional "others" row
const MAX_LEGEND_ENTRIES = 3;
const R_OUTER = 54;
const R_INNER = 34;
const CX = 64;
const CY = 64;
// Half-donut: upper semicircle, flat edge at bottom.
// Slices sweep from -90° (left) to +90° (right) = 180° total.
const HALF_DONUT_START_DEG = -90;
function domainFromEmail(email: string): string {
return email.split('@')[1] ?? email;
}
function displayLabel(entry: BlockedByConnectionEntry): string {
return entry.title ?? domainFromEmail(entry.email);
}
function polarToXY(cx: number, cy: number, r: number, angleDeg: number) {
const rad = ((angleDeg - 90) * Math.PI) / 180;
return {
x: cx + r * Math.cos(rad),
y: cy + r * Math.sin(rad),
};
}
function arcPath(
cx: number,
cy: number,
rOuter: number,
rInner: number,
startDeg: number,
endDeg: number,
): string {
const outerStart = polarToXY(cx, cy, rOuter, startDeg);
const outerEnd = polarToXY(cx, cy, rOuter, endDeg);
const innerEnd = polarToXY(cx, cy, rInner, endDeg);
const innerStart = polarToXY(cx, cy, rInner, startDeg);
const large = endDeg - startDeg > 180 ? 1 : 0;
return [
`M ${outerStart.x} ${outerStart.y}`,
`A ${rOuter} ${rOuter} 0 ${large} 1 ${outerEnd.x} ${outerEnd.y}`,
`L ${innerEnd.x} ${innerEnd.y}`,
`A ${rInner} ${rInner} 0 ${large} 0 ${innerStart.x} ${innerStart.y}`,
'Z',
].join(' ');
}
export function MailDistributionChart({ data, hero, totalBlocked, accountCount, isLegend }: Props) {
const { t } = useTranslation();
const colors = useColors();
const total = data.reduce((s, d) => s + d.count, 0);
// Build donut slices: always cap at 4 visible segments (Top-3 + Sonstige).
// Edge-case: ≤3 → no grouping. Exactly 4 → show all 4 (no grouping).
// 5+ → Top-3 + Sonstige.
const slices = useMemo(() => {
if (data.length === 0 || total === 0) return [];
const sorted = [...data].sort((a, b) => b.count - a.count);
if (sorted.length <= 4) {
return sorted.map((e, i) => ({
label: displayLabel(e),
count: e.count,
color: SLICE_COLORS[i] ?? OTHER_COLOR,
isOther: false,
hiddenCount: 0,
}));
}
// 5+ connections: Top-3 + Sonstige bucket
const top3 = sorted.slice(0, MAX_LEGEND_ENTRIES);
const rest = sorted.slice(MAX_LEGEND_ENTRIES);
const restCount = rest.reduce((s, e) => s + e.count, 0);
const restConnectionCount = rest.length;
const items = top3.map((e, i) => ({
label: displayLabel(e),
count: e.count,
color: SLICE_COLORS[i],
isOther: false,
hiddenCount: 0,
}));
items.push({
label: t('mail.stats.distribution_other_n', { n: restConnectionCount }),
count: restCount,
color: OTHER_COLOR,
isOther: true,
hiddenCount: restConnectionCount,
});
return items;
}, [data, total, t]);
if (data.length <= 1 || total === 0) return null;
let cursor = HALF_DONUT_START_DEG;
const displayTotal = totalBlocked ?? total;
if (hero) {
return (
{/* Integrated title row */}
{displayTotal.toLocaleString()}
{t('mail.stats_account_summary', { count: accountCount ?? data.length })}
{/* Live / Scheduled pill */}
{isLegend ? t('mail.live') : t('mail.scheduled')}
{/* Donut + Legend */}
{slices.map((slice) => (
))}
);
}
// Standard (non-hero) card — kept for potential reuse
return (
{t('mail.stats.distribution_heading')}
{slices.map((slice) => (
))}
);
}
function LegendRow({
slice,
colors,
}: {
slice: { label: string; count: number; color: string; isOther: boolean };
colors: ReturnType;
}) {
return (
{slice.label}
{slice.count}
);
}