import dynamic from 'next/dynamic';
import { useMemo, useState } from 'react';
import type { ApexOptions } from 'apexcharts';

import type {
    AcquisitionActiveFilters,
    AcquisitionRowClickPayload,
    AcquisitionTableRow,
    AcquisitionTableRowInput,
} from './AcquisitionTable';
import { matchesUtmSearch } from './AcquisitionTable';
import { useJourneyCountMode } from './JourneyCountModeContext';

const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false });

const OTHER_LABEL = 'Other';
const NOT_SET = '(not set)';
const OTHER_SHARE_THRESHOLD = 0.02;

/** Palet default Apex (palette1) — dipakai juga di legend channel group. */
const APEX_PALETTE = ['#008FFB', '#00A86F', '#CA8505', '#FF4560', '#846DD5'];

type SunburstMetric = 'sessions' | 'purchase_sessions';

type NodeMeta = {
    channelGroup: string;
    utmSource: string;
    isOther: boolean;
};

type SunburstNode = {
    x: string;
    y: number;
    color?: string;
    meta?: NodeMeta;
    children?: SunburstNode[];
};

const EMPTY_ROW: AcquisitionTableRow = {
    id: '',
    channel_group: '',
    utm_source: '',
    utm_medium: '',
    utm_campaign: '',
    sessions: 0,
    event_count: 0,
    engaged_sessions: 0,
    checkout_sessions: 0,
    purchase_sessions: 0,
    purchase_rate_percent: 0,
    avg_duration_min: 0,
    engaged_sessions_per_active_user: 0,
    active_users: 0,
    total_duration_ms: 0,
};

function cleanLabel(value: string, fallback: string) {
    const v = String(value || '').trim();
    return v || fallback;
}

function metricValue(row: AcquisitionTableRowInput, metric: SunburstMetric) {
    return Number(metric === 'purchase_sessions' ? row.purchase_sessions : row.sessions) || 0;
}

function rowsMatchingFilters(rows: AcquisitionTableRowInput[], filters?: AcquisitionActiveFilters) {
    if (!filters) return rows;
    const channel = filters.channel_group.trim();
    const source = filters.utm_source.trim().toLowerCase();
    const medium = filters.utm_medium.trim().toLowerCase();
    const campaign = filters.utm_campaign.trim().toLowerCase();
    if (!channel && !source && !medium && !campaign) return rows;

    return rows.filter((row) => {
        if (channel && cleanLabel(row.channel_group, 'Unassigned') !== channel) return false;
        if (source && !matchesUtmSearch(cleanLabel(row.utm_source, ''), source)) return false;
        if (medium && !matchesUtmSearch(cleanLabel(row.utm_medium, ''), medium)) return false;
        if (campaign && !matchesUtmSearch(cleanLabel(row.utm_campaign, ''), campaign)) return false;
        return true;
    });
}

function rollupOther(nodes: SunburstNode[], total: number, parentChannel = ''): SunburstNode[] {
    if (!total || nodes.length <= 1) return nodes;
    const kept: SunburstNode[] = [];
    let otherValue = 0;
    for (const node of nodes) {
        if (node.y / total < OTHER_SHARE_THRESHOLD) otherValue += node.y;
        else kept.push(node);
    }
    if (otherValue <= 0 || kept.length === 0) return nodes;
    kept.push({
        x: OTHER_LABEL,
        y: otherValue,
        meta: { channelGroup: parentChannel, utmSource: '', isOther: true },
    });
    return kept;
}

export function buildAcquisitionSunburstData(
    rows: AcquisitionTableRowInput[],
    metric: SunburstMetric,
    filters?: AcquisitionActiveFilters
): SunburstNode[] {
    const filtered = rowsMatchingFilters(rows, filters);
    const channelMap = new Map<string, { y: number; sources: Map<string, number> }>();
    let total = 0;

    for (const row of filtered) {
        const y = metricValue(row, metric);
        if (y <= 0) continue;
        const channel = cleanLabel(row.channel_group, 'Unassigned');
        const source = cleanLabel(row.utm_source, NOT_SET);
        const bucket = channelMap.get(channel) || { y: 0, sources: new Map<string, number>() };
        bucket.y += y;
        bucket.sources.set(source, (bucket.sources.get(source) || 0) + y);
        channelMap.set(channel, bucket);
        total += y;
    }

    const channels: SunburstNode[] = [...channelMap.entries()].map(([channel, bucket], index) => {
        const sourceNodes: SunburstNode[] = [...bucket.sources.entries()].map(([source, y]) => ({
            x: source,
            y,
            meta: {
                channelGroup: channel,
                utmSource: source === NOT_SET ? '' : source,
                isOther: false,
            },
        }));
        return {
            x: channel,
            y: bucket.y,
            color: APEX_PALETTE[index % APEX_PALETTE.length],
            meta: { channelGroup: channel, utmSource: '', isOther: false },
            children: rollupOther(sourceNodes, bucket.y, channel),
        };
    });

    return rollupOther(channels, total).sort((a, b) => b.y - a.y);
}

function labelsFromMorphKey(key: string) {
    return key
        .split('/')
        .filter(Boolean)
        .map((part) => part.replace(/^\d+:/, ''));
}

function nodeFromPath(tree: SunburstNode[], labels: string[]): SunburstNode | null {
    let level = tree;
    let found: SunburstNode | null = null;
    for (const label of labels) {
        found = level.find((node) => node.x === label) || null;
        if (!found) return null;
        level = found.children || [];
    }
    return found;
}

interface AcquisitionSunburstChartProps {
    rows: AcquisitionTableRowInput[];
    activeFilters?: AcquisitionActiveFilters;
    onSliceClick?: (payload: AcquisitionRowClickPayload) => void;
    isRefreshing?: boolean;
}

const AcquisitionSunburstChart = ({
    rows,
    activeFilters,
    onSliceClick,
    isRefreshing = false,
}: AcquisitionSunburstChartProps) => {
    const countMode = useJourneyCountMode();
    const [metric, setMetric] = useState<SunburstMetric>('sessions');

    const tree = useMemo(
        () => buildAcquisitionSunburstData(rows, metric, activeFilters),
        [rows, metric, activeFilters]
    );

    const options = useMemo<ApexOptions>(
        () => ({
            chart: {
                type: 'sunburst',
                fontFamily: 'Nunito, sans-serif',
                toolbar: { show: false },
            },
            colors: APEX_PALETTE,
            legend: { show: false },
            dataLabels: { enabled: false },
            plotOptions: {
                sunburst: {
                    innerSize: '25%',
                    borderRadius: 4,
                    spacing: 1,
                    zoomOnClick: true,
                    dataLabels: {
                        show: true,
                        minAngleToShow: 12,
                    },
                },
            },
        }),
        []
    );

    const chartKey = [
        metric,
        activeFilters?.channel_group || '',
        activeFilters?.utm_source || '',
        activeFilters?.utm_campaign || '',
        tree.map((n) => `${n.x}:${n.y}`).join('|'),
    ].join('::');

    const handleChartClick = (event: React.MouseEvent<HTMLDivElement>) => {
        const el = (event.target as Element | null)?.closest?.('.apexcharts-sunburst-arc');
        if (!el || !onSliceClick) return;
        const labels = labelsFromMorphKey(el.getAttribute('data:key') || '');
        const node = nodeFromPath(tree, labels);
        if (!node?.meta || node.meta.isOther) return;

        const { channelGroup, utmSource } = node.meta;
        onSliceClick({
            row: {
                ...EMPTY_ROW,
                id: `${channelGroup}|${utmSource}`,
                channel_group: channelGroup,
                utm_source: utmSource,
            },
            primaryDimension: utmSource ? 'utm_source' : 'channel_group',
            filters: {
                channel_group: channelGroup,
                utm_source: utmSource,
                utm_medium: '',
                utm_campaign: '',
            },
        });
    };

    return (
        <div className="relative">
            <div className="mb-2 flex flex-wrap items-center justify-between gap-2">
                <p className="text-xs text-gray-500">Channel group → source</p>
                <label className="flex items-center gap-2 text-xs text-gray-500">
                    Metrik
                    <select
                        className="form-select text-xs py-1 w-auto"
                        value={metric}
                        onChange={(e) => setMetric(e.target.value as SunburstMetric)}
                    >
                        <option value="sessions">{countMode === 'users' ? 'Users' : 'Sessions'}</option>
                        <option value="purchase_sessions">Purchase</option>
                    </select>
                </label>
            </div>
            {tree.length ? (
                <>
                    <div onClick={handleChartClick}>
                        <ReactApexChart
                            key={chartKey}
                            options={options}
                            series={[{ data: tree }]}
                            type={'sunburst' as 'donut'}
                            height={280}
                        />
                    </div>
                    <div className="mt-1 flex flex-wrap gap-x-3 gap-y-1">
                        {tree.map((node) => (
                            <span key={node.x} className="inline-flex items-center gap-1.5 text-[11px] text-gray-600">
                                <span
                                    className="inline-block h-2 w-2 rounded-full"
                                    style={{ backgroundColor: node.color || '#008FFB' }}
                                />
                                {node.x}
                            </span>
                        ))}
                    </div>
                </>
            ) : (
                <p className="text-sm text-gray-400 py-16 text-center">Belum ada data channel group.</p>
            )}
            {isRefreshing ? (
                <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/55 dark:bg-black/40">
                    <span className="inline-block h-7 w-7 animate-spin rounded-full border-[3px] border-primary border-l-transparent" />
                </div>
            ) : null}
        </div>
    );
};

export default AcquisitionSunburstChart;
