import { faker } from "@faker-js/faker"
import { Meta, StoryObj } from "@storybook/react"
import {
  addDays,
  addHours,
  parseISO,
  startOfDay,
  startOfToday,
  subDays,
  subMinutes,
} from "date-fns"
import { list } from "radash"
import React, { useState } from "react"
import { useDateTimeFormatter } from "../../../hooks"
import { useProminentColors } from "../../../hooks/useProminentColors/useProminentColors"
import { Button } from "../../Button"
import { List } from "../../List"
import { NumberDisplay } from "../../NumberDisplay"
import { TakeoverModal } from "../../TakeoverModal"
import { Text } from "../../Text"
import { ChartPanel } from "../ChartPanel"
import {
  FloorPriceDataPoint,
  MOCK_FLOOR_PRICE_DATA,
  MOCK_PRICE_AND_AVERAGE_PRICE,
  MOCK_VOLUME_DATA,
  PriceAndAveragePriceDataPoint,
  SCATTER_LINE_AND_VOLUME,
  VolumeDataPoint,
} from "./__mocks__/volumeData"
import { TimeSeriesChart, TimeSeriesChartProps } from "./TimeSeriesChart"

type Story = StoryObj<typeof TimeSeriesChart<VolumeDataPoint>>

const RenderTooltip = ({ item }: { item: VolumeDataPoint }) => {
  const formatDate = useDateTimeFormatter()

  return (
    <>
      <Text.Body asChild size="sm">
        <div>{formatDate(new Date(item.date))}</div>
      </Text.Body>
      <Text.Body asChild size="sm">
        <div>
          Avg. price: <NumberDisplay suffix="ETH" value={item.averagePrice} />
        </div>
      </Text.Body>
      <Text.Body asChild size="sm">
        <div>
          Volume: <NumberDisplay suffix="ETH" value={item.volume} />
        </div>
      </Text.Body>
    </>
  )
}

const meta: Meta<typeof TimeSeriesChart<VolumeDataPoint>> = {
  title: "Design System/TimeSeriesChart",
  component: TimeSeriesChart,
  args: {
    className: "h-[400px]",
    data: MOCK_VOLUME_DATA,
    getDate: (d: VolumeDataPoint) => new Date(d.date),
    getValue: (d: VolumeDataPoint) => d.averagePrice,
    renderTooltip: RenderTooltip,
  },
}

export default meta

export const AveragePrice: Story = {}

export const AveragePriceWithDelay: Story = {
  args: {
    tooltipDelay: 1_000,
  },
}

export const AreaSplineChart: Story = {
  args: {
    lineType: "areaspline",
  },
}

export const AreaChart: Story = {
  args: {
    lineType: "area",
  },
}

export const AveragePriceWithVolume: Story = {
  args: {
    getColumnValue: (d: VolumeDataPoint) => d.volume,
  },
}

const WithScatterTemplate = (
  args: TimeSeriesChartProps<PriceAndAveragePriceDataPoint>,
) => {
  return <TimeSeriesChart {...args} />
}

const LOWER_BOUND = 2000
const UPPER_BOUND = 10000

export const SalesWithAveragePriceAndOutliers = {
  render: WithScatterTemplate,
  args: {
    data: MOCK_PRICE_AND_AVERAGE_PRICE,
    getScatterValue: (d: PriceAndAveragePriceDataPoint) =>
      Math.max(LOWER_BOUND, Math.min(UPPER_BOUND, d.price)),
    getDate: (d: PriceAndAveragePriceDataPoint) => new Date(d.date),
    getValue: (d: PriceAndAveragePriceDataPoint) => d.priceMovingAverage,
    getIsOutlier: (d: PriceAndAveragePriceDataPoint) =>
      d.price != Math.max(LOWER_BOUND, Math.min(UPPER_BOUND, d.price)),
    yAxisMax: UPPER_BOUND,
    yAxisMin: LOWER_BOUND,
    formatYAxisLabels: (value: number) => `${value} ETH`,
    scatterOptions: {
      tooltipDelay: 200,
    },
  },
}

export const FixedWidth: Story = {
  args: {
    className: "w-[400px]",
  },
}

export const AxesHidden: Story = {
  args: {
    hideAxes: true,
  },
}

export const YAxisHidden: Story = {
  args: {
    hideYAxis: true,
  },
}

export const InteractiveAveragePrice: Story = {
  args: {
    interactive: true,
  },
}

export const InteractiveAveragePriceWithVolume: Story = {
  args: {
    interactive: true,
    getColumnValue: (d: VolumeDataPoint) => d.volume,
  },
}

export const ConstantSeries: Story = {
  args: {
    data: list(0, 6).map(index => ({
      volume: 100,
      averagePrice: 100,
      date: subMinutes(addHours(startOfToday(), 1), 7 - index).toISOString(),
    })),
  },
}

export const AddData: Story = {
  render: function Render(args: TimeSeriesChartProps<VolumeDataPoint>) {
    const [data, setData] = useState(args.data)
    const lastDataPoint = data[data.length - 1]

    return (
      <>
        <Button
          onClick={() =>
            setData([
              ...data,
              {
                volume: faker.number.float({
                  min: 100,
                  max: 1000,
                }),
                averagePrice: faker.number.float({
                  min: 100,
                  max: 1000,
                }),
                date: addDays(lastDataPoint.date, 1).toISOString(),
              },
            ])
          }
        >
          Add data
        </Button>
        <TimeSeriesChart {...args} data={data} />
      </>
    )
  },
}

export const Extremes: Story = {
  args: {
    ...AveragePriceWithVolume.args,
    data: list(0, 6).map(index => ({
      ...MOCK_VOLUME_DATA[index],
      date: subDays(startOfToday(), 7 - index).toISOString(),
    })),
    rangeEnd: addDays(new Date(), 1),
    rangeStart: subDays(new Date(), 10),
  },
}

const FloorPriceTemplate = (
  args: TimeSeriesChartProps<FloorPriceDataPoint>,
) => <TimeSeriesChart {...args} />

export const FloorPrice = {
  render: FloorPriceTemplate,
  args: {
    interactive: true,
    data: MOCK_FLOOR_PRICE_DATA,
    getDate: (dp: FloorPriceDataPoint) => new Date(dp.timestamp),
    getValue: (dp: FloorPriceDataPoint) => dp.price,
    renderTooltip: function RenderTooltip(d: FloorPriceDataPoint) {
      const formatDate = useDateTimeFormatter()

      return (
        <>
          <Text.Body asChild size="sm">
            <div>{formatDate(parseISO(d.timestamp))}</div>
          </Text.Body>
          <Text.Body asChild size="sm">
            <div>
              Floor price: <NumberDisplay suffix="ETH" value={d.price} />
            </div>
          </Text.Body>
        </>
      )
    },
  },
}

export const Linear = {
  render: FloorPriceTemplate,
  args: {
    ...FloorPrice.args,
    data: list(0, 450).map(index => ({
      price: index,
      timestamp: addDays(startOfDay(new Date()), index).toISOString(),
    })),
  },
}

const PanelTemplate = (args: TimeSeriesChartProps<VolumeDataPoint>) => {
  return (
    <ChartPanel>
      <ChartPanel.Header>
        <div>
          <ChartPanel.Title>Volume and price</ChartPanel.Title>
          <Text size="sm">Last 30 days</Text>
        </div>
        <ChartPanel.ResetZoom />
      </ChartPanel.Header>
      <TimeSeriesChart {...args} className="h-[200px]" />
    </ChartPanel>
  )
}

export const WithPanel: Story = {
  render: PanelTemplate,
  args: {
    ...AveragePriceWithVolume.args,
    interactive: true,
  },
}

export const WithAxisTitles: Story = {
  render: PanelTemplate,
  args: {
    ...AveragePriceWithVolume.args,
    interactive: true,
    yAxisTitle: "Average price",
    columnAxisTitle: "Volume (ETH)",
    xAxisTitle: "Time",
    formatYAxisLabels: value => `${value} ETH`,
  },
}

export const WithPanelExtremes: Story = {
  render: PanelTemplate,
  args: {
    ...Extremes.args,
    interactive: true,
  },
}

const ListTemplate = (args: TimeSeriesChartProps<VolumeDataPoint>) => {
  return (
    <List>
      {list(100).map(index => (
        <List.Item key={index}>
          <List.Item.Content>
            <List.Item.Title>Chart {index + 1}</List.Item.Title>
          </List.Item.Content>
          <List.Item.Side>
            <TimeSeriesChart className="h-[32px] w-[48px]" {...args} />
          </List.Item.Side>
        </List.Item>
      ))}
    </List>
  )
}
export const InList: Story = {
  render: ListTemplate,
  args: {
    lineWidth: 2,
    markerRadius: 2,
    hideAxes: true,
    data: MOCK_VOLUME_DATA.slice(3, 10),
    lineType: "spline",
    overrides: {
      tooltip: {
        style: {
          // Need to provide tooltip width here because highcharts limits it to the chart width
          width: 150,
        },
        // No hide delay so tooltips in list dont overlap
        hideDelay: 0,
      },
    },
  },
}

export const AreaChartInList: Story = {
  render: ListTemplate,
  args: {
    ...InList.args,
    lineType: "areaspline",
  },
}

export const ScatterLineAndVolume: StoryObj<
  typeof TimeSeriesChart<
    (typeof SCATTER_LINE_AND_VOLUME)["collectionSales"][number],
    (typeof SCATTER_LINE_AND_VOLUME)["collectionVolumes"][number] & {
      metadata: string
    }
  >
> = {
  args: {
    className: "h-[400px]",
    data: SCATTER_LINE_AND_VOLUME.collectionSales,
    getDate: d => new Date(d.date),
    getValue: d => +d.priceMovingAverage,
    getScatterValue: d => +d.price,
    scatterOptions: {
      cursor: "pointer",
      events: {
        click: () => {
          alert("You clicked a point")
        },
      },
    },
    standaloneColumn: {
      items: SCATTER_LINE_AND_VOLUME.collectionVolumes.map(d => ({
        metadata: "metadata",
        ...d,
      })),
      getDate: d => new Date(d.date),
      getValue: d => +d.volume,
    },
    renderTooltip: function RenderTooltip({ item, matchedColumnData }) {
      const formatDate = useDateTimeFormatter()

      return (
        <>
          <Text.Body asChild size="sm">
            <div>{formatDate(parseISO(item.date))}</div>
          </Text.Body>
          <Text.Body asChild size="sm">
            <div>
              Avg. price:
              <NumberDisplay suffix="ETH" value={item.priceMovingAverage} />
            </div>
          </Text.Body>
          <Text.Body asChild size="sm">
            <div>
              Price: <NumberDisplay suffix="ETH" value={item.price} />
            </div>
          </Text.Body>
          {matchedColumnData ? (
            <>
              <Text.Body asChild size="sm">
                <div>
                  Volume:
                  <NumberDisplay
                    suffix="ETH"
                    value={matchedColumnData.volume}
                  />
                </div>
              </Text.Body>
              <Text.Body asChild size="sm">
                <div>Metadata: {matchedColumnData.metadata}</div>
              </Text.Body>
            </>
          ) : null}
        </>
      )
    },
  },
}

const TakeoverModalTemplate = (args: TimeSeriesChartProps<VolumeDataPoint>) => {
  const [item, setItem] = useState<VolumeDataPoint | null>(null)
  return (
    <>
      <TimeSeriesChart {...args} />
      <TakeoverModal
        content={item?.volume.toString()}
        open={!!item}
        onOpenChange={() => setItem(null)}
      />
    </>
  )
}

export const TakeoverModalOnClick: Story = {
  render: TakeoverModalTemplate,
}

const CustomColorTemplate = (args: TimeSeriesChartProps<VolumeDataPoint>) => {
  const { prominentColors } = useProminentColors(
    "https://i2.seadn.io/ethereum/ede40ccd9bcb4528838f1d029be13224/5f1b200a067938f507cbe12bbbabc2/0d5f1b200a067938f507cbe12bbbabc2.jpeg?h=250&w=250",
  )

  return (
    <TimeSeriesChart
      {...args}
      splineOptions={{
        color: prominentColors[0],
        marker: {
          enabled: false,
          states: {
            hover: {
              radius: 4,
              fillColor: prominentColors[1],
              lineColor: prominentColors[1],
            },
          },
        },
      }}
    />
  )
}

export const CustomColors: Story = {
  render: CustomColorTemplate,
}

export const Skeleton: Story = {
  render: () => (
    <ChartPanel>
      <ChartPanel.Header>
        <ChartPanel.Title>Loadign Time Series</ChartPanel.Title>
        <ChartPanel.ResetZoom />
      </ChartPanel.Header>
      <TimeSeriesChart.Skeleton
        className="h-[200px]"
        showLine
        showScatter
        xAxisLabels={["1", "2", "3", "4"]}
        yAxisLabels={["1 ETH", "2 ETH", "3 ETH"]}
      />
    </ChartPanel>
  ),
}

export const EmptyState: Story = {
  render: () => (
    <ChartPanel>
      <ChartPanel.Header>
        <ChartPanel.Title>Empty Time Series</ChartPanel.Title>
        <ChartPanel.ResetZoom />
      </ChartPanel.Header>

      <TimeSeriesChart.EmptyState
        className="h-[200px]"
        overrides={{
          Skeleton: {
            showScatter: true,
            xAxisLabels: ["1", "2", "3", "4"],
            yAxisLabels: ["1 ETH", "2 ETH", "3 ETH"],
          },
        }}
      />
    </ChartPanel>
  ),
}

// Create type for scatter-only data points
type ScatterDataPoint = {
  date: string
  value: number
}

// Create mock data for scatter-only example
const MOCK_SCATTER_ONLY_DATA: ScatterDataPoint[] = list(20).map((_, i) => ({
  date: new Date(addDays(startOfToday(), i - 10).getTime()).toISOString(),
  value: Math.random() * 300 + 200,
}))

const ScatterOnlyTooltip = ({ item }: { item: ScatterDataPoint }) => {
  const formatDate = useDateTimeFormatter()

  return (
    <>
      <Text.Body asChild size="sm">
        <div>{formatDate(new Date(item.date))}</div>
      </Text.Body>
      <Text.Body asChild size="sm">
        <div>
          Value: <NumberDisplay value={item.value} />
        </div>
      </Text.Body>
    </>
  )
}

const ScatterOnlyTemplate = (args: TimeSeriesChartProps<ScatterDataPoint>) => {
  return <TimeSeriesChart {...args} />
}

export const ScatterOnly = {
  render: ScatterOnlyTemplate,
  args: {
    className: "h-[400px]",
    data: MOCK_SCATTER_ONLY_DATA,
    getDate: (d: ScatterDataPoint) => new Date(d.date),
    // No getValue prop - using only getScatterValue
    getScatterValue: (d: ScatterDataPoint) => d.value,
    interactive: true,
    renderTooltip: ScatterOnlyTooltip,
    scatterOptions: {
      marker: {
        enabled: true,
        radius: 5,
      },
      color: "rgb(var(--color-blue-4))",
    },
  },
}
