import type { WeeklyPoint } from '@/types';

const width = 640;
const height = 200;
const padding = { top: 20, right: 12, bottom: 34, left: 32 };

const plotWidth = width - padding.left - padding.right;
const plotHeight = height - padding.top - padding.bottom;
const baseline = padding.top + plotHeight;

const barWidth = 20;
const barGap = 4;
const cornerRadius = 4;

/** A bar with rounded top corners, square where it meets the baseline. */
function barPath(x: number, top: number, barHeight: number): string {
    const radius = Math.min(cornerRadius, barWidth / 2, barHeight);

    return [
        `M${x},${baseline}`,
        `L${x},${top + radius}`,
        `Q${x},${top} ${x + radius},${top}`,
        `L${x + barWidth - radius},${top}`,
        `Q${x + barWidth},${top} ${x + barWidth},${top + radius}`,
        `L${x + barWidth},${baseline}`,
        'Z',
    ].join(' ');
}

/** Rounds the axis top to a readable number so the gridlines land on whole lessons. */
function niceMax(value: number): number {
    if (value <= 4) {
        return Math.max(value, 1);
    }

    return Math.ceil(value / 5) * 5;
}

export default function WeeklyChart({ weekly }: { weekly: WeeklyPoint[] }) {
    if (weekly.length === 0) {
        return (
            <p className="text-muted-foreground text-sm">
                Nessuna lezione registrata nelle ultime settimane.
            </p>
        );
    }

    const max = niceMax(
        Math.max(...weekly.flatMap((point) => [point.ended, point.scheduled])),
    );
    const groupWidth = plotWidth / weekly.length;
    const pairWidth = barWidth * 2 + barGap;
    const ticks = max % 2 === 0 ? [0, max / 2, max] : [0, max];
    const lastIndex = weekly.length - 1;

    const scale = (value: number) => (value / max) * plotHeight;

    return (
        <figure className="m-0">
            {/* Below a certain width the axis labels stop being readable, so the plot
                keeps its minimum size and the container scrolls instead. */}
            <div className="overflow-x-auto">
                <svg
                    data-testid="weekly-chart"
                    viewBox={`0 0 ${width} ${height}`}
                    className="h-auto w-full min-w-[34rem]"
                    role="img"
                    aria-labelledby="weekly-chart-title weekly-chart-desc"
                >
                    <title id="weekly-chart-title">
                        Lezioni per settimana nelle ultime {weekly.length}{' '}
                        settimane
                    </title>
                    <desc id="weekly-chart-desc">
                        Due barre per settimana: lezioni terminate in tinta
                        piena e lezioni pianificate a tratteggio.
                    </desc>

                    <defs>
                        <pattern
                            id="weekly-planned"
                            width="6"
                            height="6"
                            patternUnits="userSpaceOnUse"
                            patternTransform="rotate(45)"
                        >
                            <line
                                x1="0"
                                y1="0"
                                x2="0"
                                y2="6"
                                strokeWidth="3"
                                className="stroke-muted-foreground/45"
                            />
                        </pattern>
                    </defs>

                    <g aria-hidden>
                        {ticks.map((tick) => {
                            const y = baseline - scale(tick);

                            return (
                                <g key={tick}>
                                    <line
                                        x1={padding.left}
                                        y1={y}
                                        x2={width - padding.right}
                                        y2={y}
                                        className="stroke-border"
                                    />
                                    <text
                                        x={padding.left - 6}
                                        y={y + 3}
                                        textAnchor="end"
                                        className="fill-muted-foreground text-[10px] tabular-nums"
                                    >
                                        {tick}
                                    </text>
                                </g>
                            );
                        })}
                    </g>

                    {weekly.map((point, index) => {
                        const center =
                            padding.left + groupWidth * (index + 0.5);
                        const endedX = center - pairWidth / 2;
                        const scheduledX = endedX + barWidth + barGap;
                        const endedHeight = scale(point.ended);
                        const scheduledHeight = scale(point.scheduled);
                        const isLast = index === lastIndex;

                        return (
                            <g key={point.week_start}>
                                {point.ended > 0 && (
                                    <path
                                        d={barPath(
                                            endedX,
                                            baseline - endedHeight,
                                            endedHeight,
                                        )}
                                        className="fill-foreground"
                                    >
                                        <title>{`${point.label}: ${point.ended} terminate`}</title>
                                    </path>
                                )}

                                {point.scheduled > 0 && (
                                    <path
                                        d={barPath(
                                            scheduledX,
                                            baseline - scheduledHeight,
                                            scheduledHeight,
                                        )}
                                        fill="url(#weekly-planned)"
                                        strokeWidth="1"
                                        className="stroke-muted-foreground/50"
                                    >
                                        <title>{`${point.label}: ${point.scheduled} pianificate`}</title>
                                    </path>
                                )}

                                {isLast && point.ended > 0 && (
                                    <text
                                        x={endedX + barWidth / 2}
                                        y={baseline - endedHeight - 6}
                                        textAnchor="middle"
                                        className="fill-foreground text-[10px] font-medium tabular-nums"
                                    >
                                        {point.ended}
                                    </text>
                                )}

                                {isLast && point.scheduled > 0 && (
                                    <text
                                        x={scheduledX + barWidth / 2}
                                        y={baseline - scheduledHeight - 6}
                                        textAnchor="middle"
                                        className="fill-muted-foreground text-[10px] font-medium tabular-nums"
                                    >
                                        {point.scheduled}
                                    </text>
                                )}

                                <text
                                    x={center}
                                    y={baseline + 16}
                                    textAnchor="middle"
                                    className="fill-muted-foreground text-[10px]"
                                >
                                    {point.label}
                                </text>
                            </g>
                        );
                    })}

                    <line
                        x1={padding.left}
                        y1={baseline}
                        x2={width - padding.right}
                        y2={baseline}
                        className="stroke-border"
                        aria-hidden
                    />
                </svg>
            </div>

            <figcaption className="text-muted-foreground mt-2 flex flex-wrap items-center gap-4 text-xs">
                <span className="flex items-center gap-1.5">
                    <span className="bg-foreground size-2.5 rounded-[2px]" />
                    Terminate
                </span>
                <span className="flex items-center gap-1.5">
                    <svg
                        width="10"
                        height="10"
                        className="rounded-[2px]"
                        aria-hidden
                    >
                        <defs>
                            <pattern
                                id="weekly-planned-swatch"
                                width="6"
                                height="6"
                                patternUnits="userSpaceOnUse"
                                patternTransform="rotate(45)"
                            >
                                <line
                                    x1="0"
                                    y1="0"
                                    x2="0"
                                    y2="6"
                                    strokeWidth="3"
                                    className="stroke-muted-foreground/45"
                                />
                            </pattern>
                        </defs>
                        <rect
                            width="10"
                            height="10"
                            fill="url(#weekly-planned-swatch)"
                            strokeWidth="1"
                            className="stroke-muted-foreground/50"
                        />
                    </svg>
                    Pianificate
                </span>
            </figcaption>

            <table className="sr-only">
                <caption>Lezioni per settimana</caption>
                <thead>
                    <tr>
                        <th scope="col">Settimana</th>
                        <th scope="col">Terminate</th>
                        <th scope="col">Pianificate</th>
                    </tr>
                </thead>
                <tbody>
                    {weekly.map((point) => (
                        <tr key={point.week_start}>
                            <th scope="row">{point.label}</th>
                            <td>{point.ended}</td>
                            <td>{point.scheduled}</td>
                        </tr>
                    ))}
                </tbody>
            </table>
        </figure>
    );
}
