import type { IInterval } from '@openpanel/validation';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  CartesianGrid,
  Line,
  LineChart,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from 'recharts';
import { useXAxisProps, useYAxisProps } from '../report-chart/common/axis';
import { SerieIcon } from '../report-chart/common/serie-icon';
import { OverviewLineChartTooltip } from './overview-line-chart-tooltip';
import { useNumber } from '@/hooks/use-numer-formatter';
import type { RouterOutputs } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { getChartColor } from '@/utils/theme';

type SeriesData =
  RouterOutputs['overview']['topGenericSeries']['items'][number];

interface OverviewLineChartProps {
  data: RouterOutputs['overview']['topGenericSeries'];
  interval: IInterval;
  searchQuery?: string;
  className?: string;
}

function transformDataForRecharts(
  items: SeriesData[],
  searchQuery?: string
): Array<{
  date: string;
  timestamp: number;
  [key: `${string}:sessions`]: number;
  [key: `${string}:pageviews`]: number;
  [key: `${string}:revenue`]: number | undefined;
  [key: `${string}:payload`]: {
    name: string;
    prefix?: string;
    color: string;
  };
}> {
  // Filter items by search query
  const filteredItems = searchQuery
    ? items.filter((item) => {
        const queryLower = searchQuery.toLowerCase();
        return (
          (item.name?.toLowerCase().includes(queryLower) ?? false) ||
          (item.prefix?.toLowerCase().includes(queryLower) ?? false)
        );
      })
    : items;

  // Limit to top 15
  const topItems = filteredItems.slice(0, 15);

  // Get all unique dates from all items
  const allDates = new Set<string>();
  topItems.forEach((item) => {
    item.data.forEach((d) => allDates.add(d.date));
  });

  const sortedDates = Array.from(allDates).sort();

  // Transform to recharts format
  return sortedDates.map((date) => {
    const timestamp = new Date(date).getTime();
    const result: Record<string, any> = {
      date,
      timestamp,
    };

    topItems.forEach((item, index) => {
      const dataPoint = item.data.find((d) => d.date === date);
      if (dataPoint) {
        // Use prefix:name as key to avoid collisions when same name exists with different prefixes
        const key = item.prefix ? `${item.prefix}:${item.name}` : item.name;
        result[`${key}:sessions`] = dataPoint.sessions;
        result[`${key}:pageviews`] = dataPoint.pageviews;
        if (dataPoint.revenue !== undefined) {
          result[`${key}:revenue`] = dataPoint.revenue;
        }
        result[`${key}:payload`] = {
          name: item.name,
          prefix: item.prefix,
          color: getChartColor(index),
        };
      }
    });

    return result as typeof result & {
      date: string;
      timestamp: number;
    };
  });
}

export function OverviewLineChart({
  data,
  interval,
  searchQuery,
  className,
}: OverviewLineChartProps) {
  const number = useNumber();

  const chartData = useMemo(
    () => transformDataForRecharts(data.items, searchQuery),
    [data.items, searchQuery]
  );

  const visibleItems = useMemo(() => {
    const filtered = searchQuery
      ? data.items.filter((item) => {
          const queryLower = searchQuery.toLowerCase();
          return (
            (item.name?.toLowerCase().includes(queryLower) ?? false) ||
            (item.prefix?.toLowerCase().includes(queryLower) ?? false)
          );
        })
      : data.items;
    return filtered.slice(0, 15);
  }, [data.items, searchQuery]);

  const xAxisProps = useXAxisProps({ interval, hide: false });
  const yAxisProps = useYAxisProps({});

  if (visibleItems.length === 0) {
    return (
      <div
        className={cn('flex h-[358px] items-center justify-center', className)}
      >
        <div className="text-muted-foreground text-sm">
          {searchQuery ? 'No results found' : 'No data available'}
        </div>
      </div>
    );
  }

  return (
    <div className={cn('w-full p-4', className)}>
      <div className="h-[358px] w-full">
        <OverviewLineChartTooltip.TooltipProvider interval={interval}>
          <ResponsiveContainer>
            <LineChart data={chartData}>
              <CartesianGrid
                className="stroke-border"
                horizontal={true}
                strokeDasharray="3 3"
                vertical={false}
              />
              <XAxis {...xAxisProps} />
              <YAxis {...yAxisProps} />
              <Tooltip content={<OverviewLineChartTooltip.Tooltip />} />
              {visibleItems.map((item, index) => {
                const color = getChartColor(index);
                // Use prefix:name as key to avoid collisions when same name exists with different prefixes
                const key = item.prefix
                  ? `${item.prefix}:${item.name}`
                  : item.name;
                return (
                  <Line
                    dataKey={`${key}:sessions`}
                    dot={false}
                    isAnimationActive={false}
                    key={key}
                    stroke={color}
                    strokeWidth={2}
                    type="monotone"
                  />
                );
              })}
            </LineChart>
          </ResponsiveContainer>
        </OverviewLineChartTooltip.TooltipProvider>
      </div>

      {/* Legend */}
      <LegendScrollable items={visibleItems} />
    </div>
  );
}

function LegendScrollable({ items }: { items: SeriesData[] }) {
  const scrollRef = useRef<HTMLDivElement>(null);
  const [showLeftGradient, setShowLeftGradient] = useState(false);
  const [showRightGradient, setShowRightGradient] = useState(false);

  const updateGradients = useCallback(() => {
    const el = scrollRef.current;
    if (!el) {
      return;
    }

    const { scrollLeft, scrollWidth, clientWidth } = el;
    const hasOverflow = scrollWidth > clientWidth;

    setShowLeftGradient(hasOverflow && scrollLeft > 0);
    setShowRightGradient(
      hasOverflow && scrollLeft < scrollWidth - clientWidth - 1
    );
  }, []);

  useEffect(() => {
    const el = scrollRef.current;
    if (!el) {
      return;
    }

    updateGradients();

    el.addEventListener('scroll', updateGradients);
    window.addEventListener('resize', updateGradients);

    return () => {
      el.removeEventListener('scroll', updateGradients);
      window.removeEventListener('resize', updateGradients);
    };
  }, [updateGradients]);

  // Update gradients when items change
  useEffect(() => {
    requestAnimationFrame(updateGradients);
  }, [items, updateGradients]);

  return (
    <div className="relative mt-4 -mb-2">
      {/* Left gradient */}
      <div
        className={cn(
          'pointer-events-none absolute top-0 left-0 z-10 h-full w-8 bg-gradient-to-r from-card to-transparent transition-opacity duration-200',
          showLeftGradient ? 'opacity-100' : 'opacity-0'
        )}
      />

      {/* Scrollable legend */}
      <div
        className="hide-scrollbar flex gap-x-4 gap-y-1 overflow-x-auto px-2 py-1 text-xs"
        ref={scrollRef}
      >
        {items.map((item, index) => {
          const color = getChartColor(index);
          return (
            <div
              className="flex shrink-0 items-center gap-1"
              key={item.prefix ? `${item.prefix}:${item.name}` : item.name}
              style={{ color }}
            >
              <SerieIcon name={item.prefix || item.name} />
              <span className="whitespace-nowrap font-semibold">
                {item.prefix && (
                  <>
                    <span className="text-muted-foreground">{item.prefix}</span>
                    <span className="mx-1">/</span>
                  </>
                )}
                {item.name || 'Not set'}
              </span>
            </div>
          );
        })}
      </div>

      {/* Right gradient */}
      <div
        className={cn(
          'pointer-events-none absolute top-0 right-0 z-10 h-full w-8 bg-gradient-to-l from-card to-transparent transition-opacity duration-200',
          showRightGradient ? 'opacity-100' : 'opacity-0'
        )}
      />
    </div>
  );
}

export function OverviewLineChartLoading({
  className,
}: {
  className?: string;
}) {
  return (
    <div
      className={cn('flex h-[358px] items-center justify-center', className)}
    >
      <div className="text-muted-foreground text-sm">Loading...</div>
    </div>
  );
}

export function OverviewLineChartEmpty({ className }: { className?: string }) {
  return (
    <div
      className={cn('flex h-[358px] items-center justify-center', className)}
    >
      <div className="text-muted-foreground text-sm">No data available</div>
    </div>
  );
}
