import { useMemo, useState } from "react";
import {
    Activity,
    Maximize2,
    SlidersHorizontal,
    TrendingUp,
    X,
} from "lucide-react";
import {
    Area,
    AreaChart,
    Bar,
    BarChart,
    CartesianGrid,
    Line,
    LineChart,
    ReferenceLine,
    ResponsiveContainer,
    Tooltip,
    XAxis,
    YAxis,
} from "recharts";
import type { ModuleSensorResponse } from "../../../types/meteo";

export type HistorianPoint = {
    timestamp: string;
    numericValue: number | null;
    booleanValue: boolean | null;
    textValue: string | null;
};

type Props = {
    sensor: ModuleSensorResponse | null;
    theme: "dark" | "light";
    points: HistorianPoint[];
    loading: boolean;
    hours: number;
    onHoursChange: (hours: number) => void;
    onClose: () => void;
};

const RADIUS = "rounded-[5px]";

const RANGE_OPTIONS = [
    { label: "15M", hours: 0.25 },
    { label: "30M", hours: 0.5 },
    { label: "1H", hours: 1 },
    { label: "6H", hours: 6 },
    { label: "12H", hours: 12 },
    { label: "24H", hours: 24 },
    { label: "7D", hours: 168 },
    { label: "30D", hours: 720 },
    { label: "90D", hours: 2160 },
    { label: "1Y", hours: 8760 },
];

const INTERVAL_OPTIONS = [1, 5, 15, 30, 60];

export function MeteoHistoryModal({
    sensor,
    theme,
    points,
    loading,
    hours,
    onHoursChange,
    onClose,
}: Props) {
    const isDark = theme === "dark";
    const palette = chartPalette(isDark);

    const [intervalMinutes, setIntervalMinutes] = useState(5);
    const [showIndicators, setShowIndicators] = useState(true);
    const [showCompareLine, setShowCompareLine] = useState(false);
    const [chartMode, setChartMode] = useState<"area" | "line">("area");
    const [zeroBaseline, setZeroBaseline] = useState(false);
    const [expanded, setExpanded] = useState(false);
    const [intervalOpen, setIntervalOpen] = useState(false);

    const rawData = useMemo(
        () =>
            points
                .map((point) => {
                    const value =
                        point.numericValue ??
                        (point.booleanValue === null ? null : point.booleanValue ? 1 : 0);

                    return {
                        timestamp: point.timestamp,
                        date: new Date(point.timestamp),
                        value,
                    };
                })
                .filter(
                    (
                        point,
                    ): point is {
                        timestamp: string;
                        date: Date;
                        value: number;
                    } => typeof point.value === "number",
                ),
        [points],
    );

    const chartData = useMemo(() => {
        if (rawData.length === 0) return [];

        const buckets = new Map<
            number,
            { total: number; count: number; timestamp: string }
        >();

        const intervalMs = intervalMinutes * 60 * 1000;

        for (const point of rawData) {
            const bucket = Math.floor(point.date.getTime() / intervalMs) * intervalMs;
            const current = buckets.get(bucket);

            if (current) {
                current.total += point.value;
                current.count += 1;
            } else {
                buckets.set(bucket, {
                    total: point.value,
                    count: 1,
                    timestamp: new Date(bucket).toISOString(),
                });
            }
        }

        return Array.from(buckets.entries())
            .sort(([a], [b]) => a - b)
            .map(([, bucket]) => ({
                timestamp: bucket.timestamp,
                time: new Date(bucket.timestamp).toLocaleString("pt-PT", {
                    day: "2-digit",
                    month: "2-digit",
                    hour: "2-digit",
                    minute: "2-digit",
                }),
                value: bucket.total / bucket.count,
            }));
    }, [rawData, intervalMinutes]);

    const stats = useMemo(() => {
        const values = chartData.map((point) => point.value);

        if (values.length === 0) {
            return {
                current: null,
                average: null,
                max: null,
                min: null,
                change: null,
                count: 0,
            };
        }

        const current = values[values.length - 1];
        const first = values[0];

        return {
            current,
            average: values.reduce((sum, value) => sum + value, 0) / values.length,
            max: Math.max(...values),
            min: Math.min(...values),
            change: current - first,
            count: values.length,
        };
    }, [chartData]);

    const miniBars = useMemo(() => {
        const chunkSize = Math.max(1, Math.ceil(chartData.length / 28));

        return Array.from({ length: 28 }, (_, index) => {
            const chunk = chartData.slice(index * chunkSize, (index + 1) * chunkSize);

            return {
                name: index,
                value: chunk.length,
            };
        });
    }, [chartData]);

    if (!sensor) return null;

    const unit = sensor.unit ?? "";
    const yDomain: [number | "auto", number | "auto"] = zeroBaseline
        ? [0, "auto"]
        : ["auto", "auto"];

    const Chart = chartMode === "area" ? AreaChart : LineChart;

    return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm">
            <div
                className={
                    isDark
                        ? `${expanded ? "h-[88vh] max-w-[92vw]" : "max-w-6xl"} ${RADIUS} flex w-full flex-col overflow-hidden border border-white/10 bg-[#111827] text-slate-100 shadow-2xl`
                        : `${expanded ? "h-[88vh] max-w-[92vw]" : "max-w-6xl"} ${RADIUS} flex w-full flex-col overflow-hidden border border-slate-200 bg-white text-slate-950 shadow-[0_24px_70px_rgba(15,23,42,0.18)]`
                }
            >
                <header className="flex items-start justify-between gap-5 px-6 py-4">
                    <div>
                        <p
                            className={
                                isDark
                                    ? "mb-1 text-[11px] font-bold uppercase tracking-[0.22em] text-slate-500"
                                    : "mb-1 text-[11px] font-bold uppercase tracking-[0.22em] text-slate-400"
                            }
                        >
                            Histórico
                        </p>

                        <h2 className="text-xl font-black tracking-[-0.03em]">
                            {sensor.name}
                        </h2>

                        <p className="mt-1 text-xs text-slate-500">
                            Chave: {sensor.key}
                        </p>
                    </div>

                    <button
                        type="button"
                        onClick={onClose}
                        className={
                            isDark
                                ? `${RADIUS} p-2 text-slate-400 transition hover:bg-white/5 hover:text-white`
                                : `${RADIUS} p-2 text-slate-400 transition hover:bg-slate-100 hover:text-slate-900`
                        }
                    >
                        <X className="h-5 w-5" />
                    </button>
                </header>

                <main className="flex min-h-0 flex-1 flex-col px-6 pb-5">
                    <section
                        className={
                            isDark
                                ? `${RADIUS} flex min-h-0 flex-1 flex-col border border-white/10 bg-[#0b1220] p-4`
                                : `${RADIUS} flex min-h-0 flex-1 flex-col border border-slate-200 bg-slate-50 p-4`
                        }
                    >
                        <div className="mb-4 flex flex-wrap items-center gap-2">
                            {RANGE_OPTIONS.map((range) => (
                                <button
                                    key={range.label}
                                    type="button"
                                    onClick={() => onHoursChange(range.hours)}
                                    className={
                                        hours === range.hours
                                            ? activeButtonClass(isDark)
                                            : buttonClass(isDark)
                                    }
                                >
                                    {range.label}
                                </button>
                            ))}

                            <div className="relative">
                                <button
                                    type="button"
                                    onClick={() => setIntervalOpen((value) => !value)}
                                    className={buttonClass(isDark)}
                                >
                                    Intervalo: {intervalMinutes}m
                                    <svg
                                        className={`ml-2 inline h-3 w-3 transition ${intervalOpen ? "rotate-180" : ""
                                            }`}
                                        viewBox="0 0 20 20"
                                        fill="currentColor"
                                    >
                                        <path
                                            fillRule="evenodd"
                                            d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
                                            clipRule="evenodd"
                                        />
                                    </svg>
                                </button>

                                {intervalOpen && (
                                    <div
                                        className={
                                            isDark
                                                ? `absolute left-0 top-[calc(100%+8px)] z-50 min-w-[160px] overflow-hidden ${RADIUS} border border-white/10 bg-[#111827] shadow-2xl`
                                                : `absolute left-0 top-[calc(100%+8px)] z-50 min-w-[160px] overflow-hidden ${RADIUS} border border-slate-200 bg-white shadow-xl`
                                        }
                                    >
                                        {INTERVAL_OPTIONS.map((option) => (
                                            <button
                                                key={option}
                                                type="button"
                                                onClick={() => {
                                                    setIntervalMinutes(option);
                                                    setIntervalOpen(false);
                                                }}
                                                className={
                                                    intervalMinutes === option
                                                        ? isDark
                                                            ? "flex w-full items-center justify-between bg-white/[0.06] px-4 py-3 text-left text-xs font-bold text-slate-100"
                                                            : "flex w-full items-center justify-between bg-slate-100 px-4 py-3 text-left text-xs font-bold text-slate-950"
                                                        : isDark
                                                            ? "flex w-full items-center justify-between px-4 py-3 text-left text-xs font-semibold text-slate-300 hover:bg-white/[0.04]"
                                                            : "flex w-full items-center justify-between px-4 py-3 text-left text-xs font-semibold text-slate-700 hover:bg-slate-50"
                                                }
                                            >
                                                <span>{option} minutos</span>
                                                {intervalMinutes === option && (
                                                    <div
                                                        className={
                                                            isDark
                                                                ? "h-2 w-2 rounded-full bg-slate-300"
                                                                : "h-2 w-2 rounded-full bg-slate-700"
                                                        }
                                                    />
                                                )}
                                            </button>
                                        ))}
                                    </div>
                                )}
                            </div>

                            <button
                                type="button"
                                onClick={() => setShowCompareLine((value) => !value)}
                                className={toggleClass(isDark, showCompareLine)}
                            >
                                Comparar
                            </button>

                            <button
                                type="button"
                                onClick={() => setShowIndicators((value) => !value)}
                                className={toggleClass(isDark, showIndicators)}
                            >
                                <Activity className="mr-2 inline h-4 w-4" />
                                Indicadores
                            </button>
                        </div>

                        <div className="mb-3 flex flex-wrap items-center gap-6 text-xs">
                            <span className={isDark ? "font-semibold text-slate-100" : "font-semibold text-slate-950"}>
                                <span className={isDark ? "mr-2 inline-block h-2.5 w-2.5 rounded-full bg-slate-300" : "mr-2 inline-block h-2.5 w-2.5 rounded-full bg-slate-700"} />
                                {sensor.name}
                                {unit && ` (${unit})`}
                            </span>

                            <span className={isDark ? "text-slate-400" : "text-slate-500"}>
                                Média: <b>{formatValue(stats.average, unit)}</b>
                            </span>
                            <span className={isDark ? "text-slate-400" : "text-slate-500"}>
                                Máx: <b>{formatValue(stats.max, unit)}</b>
                            </span>
                            <span className={isDark ? "text-slate-400" : "text-slate-500"}>
                                Mín: <b>{formatValue(stats.min, unit)}</b>
                            </span>

                            <div className="ml-auto flex gap-2">
                                <IconButton
                                    isDark={isDark}
                                    active={chartMode === "area"}
                                    onClick={() => setChartMode(chartMode === "area" ? "line" : "area")}
                                    icon={<TrendingUp className="h-4 w-4" />}
                                />
                                <IconButton
                                    isDark={isDark}
                                    active={zeroBaseline}
                                    onClick={() => setZeroBaseline((value) => !value)}
                                    icon={<SlidersHorizontal className="h-4 w-4" />}
                                />
                                <IconButton
                                    isDark={isDark}
                                    active={expanded}
                                    onClick={() => setExpanded((value) => !value)}
                                    icon={<Maximize2 className="h-4 w-4" />}
                                />
                            </div>
                        </div>

                        <div className={expanded ? "min-h-0 flex-1" : "h-[300px]"}>
                            {loading ? (
                                <EmptyState>A carregar histórico...</EmptyState>
                            ) : chartData.length === 0 ? (
                                <EmptyState>Sem dados históricos para este período.</EmptyState>
                            ) : (
                                <ResponsiveContainer width="100%" height="100%">
                                    <Chart data={chartData}>
                                        <defs>
                                            <linearGradient id="historyFill" x1="0" y1="0" x2="0" y2="1">
                                                <stop offset="0%" stopColor={palette.line} stopOpacity={isDark ? 0.22 : 0.12} />
                                                <stop offset="70%" stopColor={palette.line} stopOpacity={isDark ? 0.08 : 0.04} />
                                                <stop offset="100%" stopColor={palette.line} stopOpacity={0} />
                                            </linearGradient>
                                        </defs>

                                        <CartesianGrid
                                            stroke={palette.grid}
                                            strokeDasharray="4 6"
                                            vertical={false}
                                        />

                                        <XAxis
                                            dataKey="time"
                                            tick={{ fill: palette.axis, fontSize: 11 }}
                                            tickLine={false}
                                            axisLine={{ stroke: palette.axisLine }}
                                            minTickGap={42}
                                        />

                                        <YAxis
                                            domain={yDomain}
                                            tick={{ fill: palette.axis, fontSize: 11 }}
                                            tickLine={false}
                                            axisLine={{ stroke: palette.axisLine }}
                                            width={44}
                                        />

                                        <Tooltip
                                            cursor={{
                                                stroke: palette.cursor,
                                                strokeWidth: 1,
                                            }}
                                            content={({ active, payload, label }) => {
                                                if (!active || !payload?.length) return null;

                                                const value = Number(payload[0].value);

                                                return (
                                                    <div
                                                        className={
                                                            isDark
                                                                ? `${RADIUS} border border-white/10 bg-[#111827] px-4 py-3 text-sm shadow-2xl`
                                                                : `${RADIUS} border border-slate-200 bg-white px-4 py-3 text-sm text-slate-950 shadow-xl`
                                                        }
                                                    >
                                                        <p className="mb-2 text-xs text-slate-500">
                                                            {label}
                                                        </p>

                                                        <p className={isDark ? "font-bold text-slate-100" : "font-bold text-slate-950"}>
                                                            Atual: {formatValue(value, unit)}
                                                        </p>

                                                        <p className={isDark ? "mt-1 text-slate-400" : "mt-1 text-slate-500"}>
                                                            Média: {formatValue(stats.average, unit)}
                                                        </p>

                                                        <p className={isDark ? "text-slate-400" : "text-slate-500"}>
                                                            Máx: {formatValue(stats.max, unit)}
                                                        </p>

                                                        <p className={isDark ? "text-slate-400" : "text-slate-500"}>
                                                            Mín: {formatValue(stats.min, unit)}
                                                        </p>
                                                    </div>
                                                );
                                            }}
                                        />

                                        {showIndicators && stats.average !== null && (
                                            <ReferenceLine
                                                y={stats.average}
                                                stroke={palette.reference}
                                                strokeDasharray="4 5"
                                                strokeOpacity={0.42}
                                            />
                                        )}

                                        {showCompareLine && stats.current !== null && (
                                            <ReferenceLine
                                                y={stats.current}
                                                stroke={palette.compare}
                                                strokeDasharray="5 5"
                                                strokeOpacity={0.38}
                                            />
                                        )}

                                        {chartMode === "area" ? (
                                            <Area
                                                type="monotone"
                                                dataKey="value"
                                                stroke={palette.line}
                                                strokeWidth={2}
                                                fill="url(#historyFill)"
                                                dot={false}
                                                activeDot={{
                                                    r: 5,
                                                    fill: palette.line,
                                                    stroke: isDark ? "#111827" : "#ffffff",
                                                    strokeWidth: 2,
                                                }}
                                                isAnimationActive={false}
                                            />
                                        ) : (
                                            <Line
                                                type="monotone"
                                                dataKey="value"
                                                stroke={palette.line}
                                                strokeWidth={2}
                                                dot={false}
                                                activeDot={{
                                                    r: 5,
                                                    fill: palette.line,
                                                    stroke: isDark ? "#111827" : "#ffffff",
                                                    strokeWidth: 2,
                                                }}
                                                isAnimationActive={false}
                                            />
                                        )}
                                    </Chart>
                                </ResponsiveContainer>
                            )}
                        </div>
                    </section>

                    {showIndicators && (
                        <section className="mt-3 grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-5">
                            <MetricCard theme={theme} title="Valor atual" value={formatValue(stats.current, unit)} sub="Agora há pouco" />
                            <MetricCard theme={theme} title="Variação" value={formatSignedValue(stats.change, unit)} sub="No período" positive={(stats.change ?? 0) >= 0} />
                            <MetricCard theme={theme} title="Máxima" value={formatValue(stats.max, unit)} sub="No período" />
                            <MetricCard theme={theme} title="Mínima" value={formatValue(stats.min, unit)} sub="No período" />

                            <div className={cardClass(isDark)}>
                                <p className="text-xs text-slate-500">
                                    Volume de Dados
                                </p>
                                <h3 className="mt-1 text-xl font-black">
                                    {stats.count.toLocaleString("pt-PT")}
                                </h3>
                                <p className="text-xs text-slate-500">
                                    pontos
                                </p>

                                <div className="mt-2 h-9">
                                    <ResponsiveContainer width="100%" height="100%">
                                        <BarChart data={miniBars}>
                                            <Bar
                                                dataKey="value"
                                                fill={isDark ? "#94a3b8" : "#64748b"}
                                                radius={[3, 3, 0, 0]}
                                            />
                                        </BarChart>
                                    </ResponsiveContainer>
                                </div>
                            </div>
                        </section>
                    )}
                </main>
            </div>
        </div>
    );
}

function chartPalette(isDark: boolean) {
    return isDark
        ? {
            line: "#cbd5e1",
            grid: "rgba(148,163,184,0.16)",
            axis: "#64748b",
            axisLine: "rgba(148,163,184,0.18)",
            cursor: "#64748b",
            reference: "#94a3b8",
            compare: "#64748b",
        }
        : {
            line: "#475569",
            grid: "#e2e8f0",
            axis: "#64748b",
            axisLine: "#cbd5e1",
            cursor: "#94a3b8",
            reference: "#64748b",
            compare: "#94a3b8",
        };
}

function EmptyState({ children }: { children: string }) {
    return (
        <div className="flex h-full items-center justify-center text-sm text-slate-400">
            {children}
        </div>
    );
}

function IconButton({
    isDark,
    active,
    onClick,
    icon,
}: {
    isDark: boolean;
    active?: boolean;
    onClick?: () => void;
    icon: React.ReactNode;
}) {
    return (
        <button type="button" onClick={onClick} className={toggleIconClass(isDark, Boolean(active))}>
            {icon}
        </button>
    );
}

function MetricCard({
    theme,
    title,
    value,
    sub,
}: {
    theme: "dark" | "light";
    title: string;
    value: string;
    sub: string;
    positive?: boolean;
}) {
    const isDark = theme === "dark";

    return (
        <div className={cardClass(isDark)}>
            <p className="text-xs text-slate-500">{title}</p>
            <h3 className="mt-1 text-xl font-black">{value}</h3>
            <p className="text-xs text-slate-500">{sub}</p>
        </div>
    );
}

function buttonClass(isDark: boolean) {
    return isDark
        ? `${RADIUS} border border-white/10 bg-white/[0.03] px-3 py-2 text-xs font-semibold text-slate-300 transition hover:bg-white/[0.06] hover:text-slate-100`
        : `${RADIUS} border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-600 transition hover:bg-slate-100 hover:text-slate-950`;
}

function activeButtonClass(isDark: boolean) {
    return isDark
        ? `${RADIUS} border border-white/10 bg-slate-200 px-3 py-2 text-xs font-black text-slate-950`
        : `${RADIUS} border border-slate-300 bg-slate-900 px-3 py-2 text-xs font-black text-white`;
}

function iconButtonClass(isDark: boolean) {
    return isDark
        ? `${RADIUS} border border-white/10 bg-white/[0.03] p-2 text-slate-400 transition hover:bg-white/[0.06] hover:text-slate-100`
        : `${RADIUS} border border-slate-200 bg-white p-2 text-slate-500 transition hover:bg-slate-100 hover:text-slate-950`;
}

function toggleClass(isDark: boolean, active: boolean) {
    if (active) return activeButtonClass(isDark);
    return buttonClass(isDark);
}

function toggleIconClass(isDark: boolean, active: boolean) {
    if (active) {
        return isDark
            ? `${RADIUS} border border-white/10 bg-slate-200 p-2 text-slate-950`
            : `${RADIUS} border border-slate-300 bg-slate-900 p-2 text-white`;
    }

    return iconButtonClass(isDark);
}

function cardClass(isDark: boolean) {
    return isDark
        ? `${RADIUS} border border-white/10 bg-[#111827] p-3`
        : `${RADIUS} border border-slate-200 bg-white p-3`;
}

function formatValue(value: number | null, unit: string) {
    if (value === null || Number.isNaN(value)) return "--";
    return `${value.toFixed(1)}${unit ? ` ${unit}` : ""}`;
}

function formatSignedValue(value: number | null, unit: string) {
    if (value === null || Number.isNaN(value)) return "--";

    const prefix = value >= 0 ? "+" : "";
    return `${prefix}${value.toFixed(1)}${unit ? ` ${unit}` : ""}`;
}