import * as React from 'react';
import * as RechartsPrimitive from 'recharts';
import { cn } from '@/lib/utils';

const THEMES = { light: '', dark: '.dark' } as const;

export type ChartConfig = Record<
    string,
    {
        label?: React.ReactNode;
        icon?: React.ComponentType;
    } & (
        | { color?: string; theme?: never }
        | { color?: never; theme: Record<keyof typeof THEMES, string> }
    )
>;

type ChartContextProps = {
    config: ChartConfig;
};

const ChartContext = React.createContext<ChartContextProps | null>(null);

function useChart() {
    const context = React.useContext(ChartContext);

    if (!context) {
        throw new Error('useChart must be used within a <ChartContainer />');
    }

    return context;
}

export function ChartContainer({
    id,
    className,
    children,
    config,
    ...props
}: React.ComponentProps<'div'> & {
    config: ChartConfig;
    children: React.ComponentProps<
        typeof RechartsPrimitive.ResponsiveContainer
    >['children'];
}) {
    const uniqueId = React.useId();
    const chartId = `chart-${id ?? uniqueId.replace(/:/g, '')}`;

    return (
        <ChartContext.Provider value={{ config }}>
            <div
                data-chart={chartId}
                className={cn(
                    "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
                    className,
                )}
                {...props}
            >
                <ChartStyle id={chartId} config={config} />
                <RechartsPrimitive.ResponsiveContainer>
                    {children}
                </RechartsPrimitive.ResponsiveContainer>
            </div>
        </ChartContext.Provider>
    );
}

function ChartStyle({ id, config }: { id: string; config: ChartConfig }) {
    const colorConfig = Object.entries(config).filter(
        ([, itemConfig]) => itemConfig.theme || itemConfig.color,
    );

    if (colorConfig.length === 0) {
        return null;
    }

    return (
        <style
            dangerouslySetInnerHTML={{
                __html: Object.entries(THEMES)
                    .map(
                        ([theme, prefix]) => `
${prefix} [data-chart=${id}] {
${colorConfig
    .map(([key, itemConfig]) => {
        const color =
            itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??
            itemConfig.color;

        return color ? `  --color-${key}: ${color};` : null;
    })
    .join('\n')}
}
`,
                    )
                    .join('\n'),
            }}
        />
    );
}

export const ChartTooltip = RechartsPrimitive.Tooltip;

export function ChartTooltipContent({
    active,
    payload,
    label,
    hideLabel = false,
    hideIndicator = false,
    indicator = 'dot',
    labelFormatter,
    formatter,
    className,
}: React.ComponentProps<'div'> & {
    active?: boolean;
    payload?: Array<{
        name?: string;
        value?: number | string;
        dataKey?: string | number;
        color?: string;
        payload?: Record<string, unknown>;
    }>;
    label?: string;
    hideLabel?: boolean;
    hideIndicator?: boolean;
    indicator?: 'line' | 'dot' | 'dashed';
    labelFormatter?: (value: string) => React.ReactNode;
    formatter?: (
        value: number | string,
        name: string,
        item: NonNullable<typeof payload>[number],
    ) => React.ReactNode;
}) {
    const { config } = useChart();

    if (!active || !payload?.length) {
        return null;
    }

    return (
        <div
            className={cn(
                'grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl',
                className,
            )}
        >
            {!hideLabel && label ? (
                <div className="font-medium">
                    {labelFormatter ? labelFormatter(label) : label}
                </div>
            ) : null}
            <div className="grid gap-1.5">
                {payload.map((item) => {
                    const key = String(item.dataKey ?? item.name ?? 'value');
                    const itemConfig = config[key];
                    const indicatorColor = item.color ?? `var(--color-${key})`;

                    return (
                        <div
                            key={key}
                            className="flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground"
                        >
                            {!hideIndicator ? (
                                <div
                                    className={cn(
                                        'shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)',
                                        {
                                            'h-2.5 w-2.5': indicator === 'dot',
                                            'w-1': indicator === 'line',
                                            'w-0 border-[1.5px] border-dashed bg-transparent':
                                                indicator === 'dashed',
                                        },
                                    )}
                                    style={
                                        {
                                            '--color-bg': indicatorColor,
                                            '--color-border': indicatorColor,
                                        } as React.CSSProperties
                                    }
                                />
                            ) : null}
                            <div className="flex flex-1 items-center justify-between leading-none">
                                <span className="text-muted-foreground">
                                    {itemConfig?.label ?? item.name ?? key}
                                </span>
                                <span className="font-mono font-medium tabular-nums text-foreground">
                                    {formatter
                                        ? formatter(
                                              item.value ?? 0,
                                              key,
                                              item,
                                          )
                                        : item.value}
                                </span>
                            </div>
                        </div>
                    );
                })}
            </div>
        </div>
    );
}

export const ChartLegend = RechartsPrimitive.Legend;

export function ChartLegendContent({
    className,
    hideIcon = false,
    payload,
}: React.ComponentProps<'div'> & {
    hideIcon?: boolean;
    payload?: Array<{
        value?: string;
        dataKey?: string | number;
        color?: string;
    }>;
}) {
    const { config } = useChart();

    if (!payload?.length) {
        return null;
    }

    return (
        <div
            className={cn(
                'flex flex-wrap items-center justify-center gap-4 pt-3',
                className,
            )}
        >
            {payload.map((item) => {
                const key = String(item.dataKey ?? item.value ?? '');
                const itemConfig = config[key];

                return (
                    <div
                        key={key}
                        className="flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
                    >
                        {!hideIcon ? (
                            <div
                                className="h-2 w-2 shrink-0 rounded-[2px]"
                                style={{
                                    backgroundColor:
                                        item.color ?? `var(--color-${key})`,
                                }}
                            />
                        ) : null}
                        <span className="text-muted-foreground">
                            {itemConfig?.label ?? item.value}
                        </span>
                    </div>
                );
            })}
        </div>
    );
}
