import { ColorSquare } from '@/components/color-square';
import { Button } from '@/components/ui/button';
import { pushModal } from '@/modals';
import type { RouterOutputs } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { ChevronRightIcon, InfoIcon, UsersIcon } from 'lucide-react';

import { alphabetIds } from '@openpanel/constants';

import { createChartTooltip } from '@/components/charts/chart-tooltip';
import { BarShapeBlue, BarShapeProps } from '@/components/charts/common-bar';
import { Tooltiper } from '@/components/ui/tooltip';
import { WidgetTable } from '@/components/widget-table';
import { useNumber } from '@/hooks/use-numer-formatter';
import { getChartColor, getChartTranslucentColor } from '@/utils/theme';
import { getPreviousMetric } from '@openpanel/common';
import {
  Bar,
  BarChart,
  CartesianGrid,
  Cell,
  ResponsiveContainer,
  XAxis,
  YAxis,
} from 'recharts';
import { useXAxisProps, useYAxisProps } from '../common/axis';
import { PreviousDiffIndicatorPure } from '../common/previous-diff-indicator';
import { useReportChartContext } from '../context';

type Props = {
  data: {
    current: RouterOutputs['chart']['funnel']['current'][number];
    previous: RouterOutputs['chart']['funnel']['current'][number] | null;
  };
};

export const Metric = ({
  label,
  value,
  enhancer,
  className,
}: {
  label: string;
  value: React.ReactNode;
  enhancer?: React.ReactNode;
  className?: string;
}) => (
  <div className={cn('gap-1 justify-between flex-1 col', className)}>
    <div className="text-sm text-muted-foreground">{label}</div>
    <div className="row items-center gap-2 justify-between">
      <div className="font-mono font-semibold">{value}</div>
      {enhancer && <div>{enhancer}</div>}
    </div>
  </div>
);

export function Summary({ data }: { data: RouterOutputs['chart']['funnel'] }) {
  const number = useNumber();
  const highestConversion = data.current
    .slice(0)
    .sort((a, b) => b.lastStep.percent - a.lastStep.percent)[0];
  const highestCount = data.current
    .slice(0)
    .sort((a, b) => b.lastStep.count - a.lastStep.count)[0];
  return (
    <div className="grid grid-cols-2 gap-4">
      {highestConversion && (
        <div className="card row items-center p-4 py-3">
          <Metric
            label="Highest conversion rate"
            value={
              <ChartName breakdowns={highestConversion.breakdowns ?? []} />
            }
          />
          <span className="text-xl font-semibold font-mono">
            {number.formatWithUnit(
              highestConversion.lastStep.percent / 100,
              '%',
            )}
          </span>
        </div>
      )}
      {highestCount && (
        <div className="card row items-center p-4 py-3">
          <Metric
            label="Most conversions"
            value={<ChartName breakdowns={highestCount.breakdowns ?? []} />}
          />
          <span className="text-xl font-semibold font-mono">
            {number.format(highestCount.lastStep.count)}
          </span>
        </div>
      )}
    </div>
  );
}

function ChartName({
  breakdowns,
  className,
}: { breakdowns: string[]; className?: string }) {
  return (
    <div className={cn('flex items-center gap-2 font-medium', className)}>
      {breakdowns.map((name, index) => {
        return (
          <>
            {index !== 0 && <ChevronRightIcon className="size-3" />}
            <span key={name}>{name}</span>
          </>
        );
      })}
    </div>
  );
}

export function Tables({
  data: {
    current: { steps, mostDropoffsStep, lastStep, breakdowns },
    previous: previousData,
  },
}: Props) {
  const number = useNumber();
  const hasHeader = breakdowns.length > 0;
  const {
    report: {
      projectId,
      startDate,
      endDate,
      range,
      interval,
      series: reportSeries,
      breakdowns: reportBreakdowns,
      previous,
      options,
    },
  } = useReportChartContext();

  const funnelOptions = options?.type === 'funnel' ? options : undefined;
  const funnelWindow = funnelOptions?.funnelWindow;
  const funnelGroup = funnelOptions?.funnelGroup;

  const handleInspectStep = (step: (typeof steps)[0], stepIndex: number) => {
    if (!projectId || !step.event.id) return;

    // For funnels, we need to pass the step index so the modal can query
    // users who completed at least that step in the funnel sequence
      pushModal('ViewChartUsers', {
        type: 'funnel',
        report: {
          projectId,
          series: reportSeries,
          breakdowns: reportBreakdowns || [],
          interval: interval || 'day',
          startDate,
          endDate,
          range,
          previous,
          chartType: 'funnel',
          metric: 'sum',
          options: funnelOptions,
        },
        stepIndex, // Pass the step index for funnel queries
      });
  };
  return (
    <div className={cn('col @container divide-y divide-border card')}>
      {hasHeader && <ChartName breakdowns={breakdowns} className="p-4 py-3" />}
      <div className={cn('bg-def-100', !hasHeader && 'rounded-t-md')}>
        <div className="col max-md:divide-y md:row md:items-center md:divide-x divide-border">
          <Metric
            className="p-4 py-3"
            label="Conversion"
            value={number.formatWithUnit(lastStep?.percent / 100, '%')}
            enhancer={
              previousData && (
                <PreviousDiffIndicatorPure
                  {...getPreviousMetric(
                    lastStep?.percent,
                    previousData.lastStep?.percent,
                  )}
                />
              )
            }
          />
          <Metric
            className="p-4 py-3"
            label="Completed"
            value={number.format(lastStep?.count)}
            enhancer={
              previousData && (
                <PreviousDiffIndicatorPure
                  {...getPreviousMetric(
                    lastStep?.count,
                    previousData.lastStep?.count,
                  )}
                />
              )
            }
          />
          {!!mostDropoffsStep && (
            <Metric
              className="p-4 py-3"
              label="Most dropoffs after"
              value={mostDropoffsStep?.event?.displayName}
              enhancer={
                <Tooltiper
                  tooltipClassName="max-w-xs"
                  content={
                    <span>
                      <span className="font-semibold">
                        {mostDropoffsStep?.dropoffCount}
                      </span>{' '}
                      dropped after this event. Improve this step and your
                      conversion rate will likely increase.
                    </span>
                  }
                >
                  <InfoIcon className="size-3" />
                </Tooltiper>
              }
            />
          )}
        </div>
      </div>
      <div className="col divide-y divide-def-200">
        <WidgetTable
          data={steps}
          keyExtractor={(item) => item.event.id!}
          className={'text-sm @container'}
          columnClassName="px-2 group/row items-center"
          eachRow={(item, index) => {
            return (
              <div className="absolute inset-px !p-0">
                <div
                  className={cn(
                    'h-full bg-def-300 group-hover/row:bg-blue-200 dark:group-hover/row:bg-blue-900 transition-colors relative',
                    item.isHighestDropoff && [
                      'bg-red-500/20',
                      'group-hover/row:bg-red-500/70',
                    ],
                    index === steps.length - 1 && 'rounded-bl-sm',
                  )}
                  style={{
                    width: `${item.percent}%`,
                  }}
                />
              </div>
            );
          }}
          columns={[
            {
              name: 'Event',
              render: (item, index) => (
                <div className="row items-center gap-2 min-w-0 relative">
                  <ColorSquare color={getChartColor(index)}>
                    {alphabetIds[index]}
                  </ColorSquare>
                  <span className="truncate">{item.event.displayName}</span>
                </div>
              ),
              width: 'w-full',
              className: 'text-left font-mono font-semibold',
            },
            {
              name: 'Completed',
              render: (item) => number.format(item.count),
              className: 'text-right font-mono hidden @xl:block',
              width: '82px',
            },
            {
              name: 'Dropped after',
              render: (item) =>
                item.dropoffCount !== null && item.dropoffPercent !== null
                  ? number.format(item.dropoffCount)
                  : null,
              className: 'text-right font-mono hidden @xl:block',
              width: '110px',
            },
            {
              name: 'Conversion',
              render: (item) => number.formatWithUnit(item.percent / 100, '%'),
              className: 'text-right font-mono font-semibold',
              width: '90px',
            },
            {
              name: '',
              render: (item) => (
                <Button
                  variant="ghost"
                  size="sm"
                  className="h-8 w-8 p-0"
                  onClick={(e) => {
                    e.stopPropagation();
                    const stepIndex = steps.findIndex(
                      (s) => s.event.id === item.event.id,
                    );
                    handleInspectStep(item, stepIndex);
                  }}
                  title="View users who completed this step"
                >
                  <UsersIcon size={16} />
                </Button>
              ),
              className: 'text-right',
              width: '48px',
            },
          ]}
        />
      </div>
    </div>
  );
}

type RechartData = {
  name: string;
  [key: `step:percent:${number}`]: number | null;
  [key: `step:data:${number}`]:
    | (RouterOutputs['chart']['funnel']['current'][number] & {
        step: RouterOutputs['chart']['funnel']['current'][number]['steps'][number];
      })
    | null;
  [key: `prev_step:percent:${number}`]: number | null;
  [key: `prev_step:data:${number}`]:
    | (RouterOutputs['chart']['funnel']['current'][number] & {
        step: RouterOutputs['chart']['funnel']['current'][number]['steps'][number];
      })
    | null;
};

const useRechartData = ({
  current,
  previous,
}: RouterOutputs['chart']['funnel']): RechartData[] => {
  const firstFunnel = current[0];
  return (
    firstFunnel?.steps.map((step, stepIndex) => {
      return {
        id: step?.event.id ?? '',
        name: step?.event.displayName ?? '',
        ...current.reduce((acc, item, index) => {
          const diff = previous?.[index];
          return {
            ...acc,
            [`step:percent:${index}`]: item.steps[stepIndex]?.percent ?? null,
            [`step:data:${index}`]: {
              ...item,
              step: item.steps[stepIndex],
            },
            [`prev_step:percent:${index}`]:
              diff?.steps[stepIndex]?.percent ?? null,
            [`prev_step:data:${index}`]: diff
              ? {
                  ...diff,
                  step: diff?.steps?.[stepIndex],
                }
              : null,
          };
        }, {}),
      };
    }) ?? []
  );
};

export function Chart({ data }: { data: RouterOutputs['chart']['funnel'] }) {
  const rechartData = useRechartData(data);
  const xAxisProps = useXAxisProps();
  const yAxisProps = useYAxisProps();
  const hasBreakdowns = data.current.length > 1;

  return (
    <TooltipProvider data={data.current}>
      <div className="aspect-video max-h-[250px] w-full p-4 card pb-1">
        <ResponsiveContainer>
          <BarChart data={rechartData}>
            <CartesianGrid
              strokeDasharray="3 3"
              horizontal={true}
              vertical={true}
              className="stroke-border"
            />
            <XAxis
              {...xAxisProps}
              dataKey="id"
              allowDuplicatedCategory={false}
              type={'category'}
              scale="auto"
              domain={undefined}
              interval="preserveStartEnd"
              tickSize={0}
              tickMargin={4}
              tickFormatter={(id) =>
                data.current[0].steps.find((step) => step.event.id === id)
                  ?.event.displayName ?? ''
              }
            />
            <YAxis {...yAxisProps} />
            {hasBreakdowns ? (
              data.current.map((item, breakdownIndex) => (
                <Bar
                  key={`step:percent:${item.id}`}
                  dataKey={`step:percent:${breakdownIndex}`}
                  shape={<BarShapeProps />}
                >
                  {rechartData.map((item, stepIndex) => (
                    <Cell
                      key={`${item.name}-${breakdownIndex}`}
                      fill={getChartTranslucentColor(breakdownIndex)}
                      stroke={getChartColor(breakdownIndex)}
                    />
                  ))}
                </Bar>
              ))
            ) : (
              <Bar
                data={rechartData}
                dataKey="step:percent:0"
                shape={<BarShapeProps />}
              >
                {rechartData.map((item, index) => (
                  <Cell
                    key={item.name}
                    fill={getChartTranslucentColor(index)}
                    stroke={getChartColor(index)}
                  />
                ))}
              </Bar>
            )}
            <Tooltip />
          </BarChart>
        </ResponsiveContainer>
      </div>
    </TooltipProvider>
  );
}

const { Tooltip, TooltipProvider } = createChartTooltip<
  RechartData,
  {
    data: RouterOutputs['chart']['funnel']['current'];
  }
>(({ data: dataArray, context, ...props }) => {
  const data = dataArray[0]!;
  const number = useNumber();
  const variants = Object.keys(data).filter((key) =>
    key.startsWith('step:data:'),
  ) as `step:data:${number}`[];

  const index = context.data[0].steps.findIndex(
    (step) => step.event.id === (data as any).id,
  );

  return (
    <>
      <div className="flex justify-between gap-8 text-muted-foreground">
        <div>{data.name}</div>
      </div>
      {variants.map((key, breakdownIndex) => {
        const variant = data[key];
        const prevVariant = data[`prev_${key}`];
        if (!variant?.step) {
          return null;
        }
        return (
          <div className="row gap-2" key={key}>
            <div
              className="w-[3px] rounded-full"
              style={{
                background: getChartColor(
                  variants.length > 1 ? breakdownIndex : index,
                ),
              }}
            />
            <div className="col flex-1 gap-1">
              <div className="flex items-center gap-1">
                <ChartName breakdowns={variant.breakdowns ?? []} />
              </div>
              <div className="flex justify-between gap-8 font-mono font-medium">
                <div className="col gap-1">
                  <span>
                    {number.formatWithUnit(variant.step.percent / 100, '%')}
                  </span>
                  <span className="text-muted-foreground">
                    ({number.format(variant.step.count)})
                  </span>
                </div>

                <PreviousDiffIndicatorPure
                  {...getPreviousMetric(
                    variant.step.percent,
                    prevVariant?.step.percent,
                  )}
                />
              </div>
            </div>
          </div>
        );
      })}
    </>
  );
});
