import React, { useMemo, useState, useEffect } from "react";
import { motion } from "framer-motion";

type DataPoint = {
  x: number | Date | string;
  y: number;
  label?: string | number;
};

type LineChartProps = {
  data: DataPoint[];
  className?: string;
  margin?: { top: number; right: number; bottom: number; left: number };
  strokeWidth?: number;
  showGrid?: boolean;
  showDots?: boolean;
  dotSize?: number;
  showArea?: boolean;
  xAxisLabel?: string;
  yAxisLabel?: string;
  animate?: boolean;
  curveType?: "linear" | "monotone" | "step";
};

const getSmoothPath = (points: { x: number; y: number }[]) => {
  let path = `M${points[0].x},${points[0].y}`;

  for (let i = 1; i < points.length; i++) {
    const prev = points[i - 1];
    const curr = points[i];
    const next = points[i + 1];

    const controlX1 = prev.x + (curr.x - prev.x) * 0.3;
    const controlX2 = curr.x - (next ? (next.x - curr.x) * 0.3 : 0);

    path += ` C${controlX1},${prev.y} ${controlX2},${curr.y} ${curr.x},${curr.y}`;
  }

  return path;
};

const LineChart: React.FC<LineChartProps> = ({
  data,
  className = "w-full h-64 md:h-96",
  margin = { top: 20, right: 20, bottom: 40, left: 40 },
  strokeWidth = 2,
  showGrid = true,
  showDots = true,
  dotSize = 4,
  showArea = true,
  xAxisLabel,
  yAxisLabel,
  animate = true,
  curveType = "monotone",
}) => {
  const [hoveredPoint, setHoveredPoint] = useState<DataPoint | null>(null);
  const [tooltipPos, setTooltipPos] = useState({ x: 0, y: 0 });
  const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null);
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

  useEffect(() => {
    const handleResize = () => {
      if (containerRef) {
        setDimensions({
          width: containerRef.clientWidth,
          height: containerRef.clientHeight,
        });
      }
    };

    handleResize();
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, [containerRef]);

  const width = dimensions.width;
  const height = dimensions.height;
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  const { minX, maxX, minY, maxY } = useMemo(() => {
    const xValues = data.map((d, i) =>
      typeof d.x === "number" || d.x instanceof Date
        ? d.x instanceof Date
          ? d.x.getTime()
          : d.x
        : i
    );
    const yValues = data.map((d) => d.y);
    return {
      minX: Math.min(...xValues),
      maxX: Math.max(...xValues),
      minY: Math.min(0, ...yValues),
      maxY: Math.max(...yValues),
    };
  }, [data]);

  const scaleX = (value: number | Date | string) => {
    if (typeof value === "string") {
      const index = data.findIndex((d) => d.x === value);
      return (index / (data.length - 1)) * innerWidth;
    }
    const numericValue = value instanceof Date ? value.getTime() : value;
    return ((numericValue - minX) / (maxX - minX)) * innerWidth;
  };

  const scaleY = (value: number) => {
    return innerHeight - ((value - minY) / (maxY - minY)) * innerHeight;
  };

  const linePath = useMemo(() => {
    if (data.length === 0) return "";

    const points = data.map((point) => ({
      x: scaleX(point.x),
      y: scaleY(point.y),
    }));

    if (curveType === "linear") {
      return `M${points.map((p) => `${p.x},${p.y}`).join(" L")}`;
    } else if (curveType === "step") {
      let path = `M${points[0].x},${points[0].y}`;
      for (let i = 1; i < points.length; i++) {
        path += ` H${(points[i].x + points[i - 1].x) / 2}`;
        path += ` V${points[i].y}`;
        path += ` H${points[i].x}`;
      }
      return path;
    } else {
      return getSmoothPath(points);
    }
  }, [data, scaleX, scaleY, curveType]);

  const areaPath = useMemo(() => {
    if (data.length === 0 || !showArea) return "";

    const points = data.map((point) => ({
      x: scaleX(point.x),
      y: scaleY(point.y),
    }));

    let path;
    if (curveType === "linear") {
      path = `M${points[0].x},${innerHeight} L${points
        .map((p) => `${p.x},${p.y}`)
        .join(" L")} L${points[points.length - 1].x},${innerHeight} Z`;
    } else if (curveType === "step") {
      path = `M${points[0].x},${innerHeight} L${points[0].x},${points[0].y}`;
      for (let i = 1; i < points.length; i++) {
        path += ` H${(points[i].x + points[i - 1].x) / 2}`;
        path += ` V${points[i].y}`;
        path += ` H${points[i].x}`;
      }
      path += ` V${innerHeight} Z`;
    } else {
      path = `M${points[0].x},${innerHeight} L${points[0].x},${points[0].y}`;
      path += getSmoothPath(points).substring(1);
      path += ` L${points[points.length - 1].x},${innerHeight} Z`;
    }

    return path;
  }, [data, scaleX, scaleY, showArea, innerHeight, curveType]);

  const formatXLabel = (value: number | Date | string) => {
    if (typeof value === "string") return value;
    if (value instanceof Date) return value.toLocaleDateString();
    return value.toString();
  };

  const formatYLabel = (value: number) => {
    if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
    if (value >= 1000) return `${(value / 1000).toFixed(1)}K`;
    return value.toFixed(2);
  };

  const xTicks = useMemo(() => {
    if (data.length <= 6) {
      return data.map((point) => ({
        x: scaleX(point.x),
        value: point.x,
      }));
    }

    const tickCount = Math.min(5, data.length);
    return Array.from({ length: tickCount }).map((_, i) => {
      const index = Math.floor((i / (tickCount - 1)) * (data.length - 1));
      const point = data[index];
      return {
        x: scaleX(point.x),
        value: point.x,
      };
    });
  }, [data, scaleX]);

  const yTicks = useMemo(() => {
    const tickCount = 5;
    return Array.from({ length: tickCount }).map((_, i) => {
      const value = minY + (i / (tickCount - 1)) * (maxY - minY);
      return {
        y: scaleY(value),
        value,
      };
    });
  }, [minY, maxY, scaleY]);

  const handlePointHover = (point: DataPoint) => {
    setHoveredPoint(point);

    const xPos = scaleX(point.x) + margin.left;
    const yPos = scaleY(point.y) + margin.top - 20;

    setTooltipPos({
      x: xPos,
      y: yPos,
    });
  };

  if (data.length === 0) {
    return (
      <div
        className={`flex items-center justify-center rounded-lg bg-gray-50 dark:bg-gray-800 ${className}`}
      >
        <p className="text-gray-500 dark:text-gray-400">اطلاعات موجود نیست</p>
      </div>
    );
  }

  return (
    <div ref={setContainerRef} className={`relative ${className}`}>
      <svg
        width={width}
        height={height}
        className="rounded-lg bg-white dark:bg-gray-900 shadow-sm dark:shadow-none dark:border dark:border-gray-700"
      >
        <rect
          width={width}
          height={height}
          rx="8"
          className="fill-white dark:fill-dark-900"
        />

        <g transform={`translate(${margin.left},${margin.top})`}>
          {showGrid && (
            <>
              {xTicks.map((tick, i) => (
                <g key={`x-grid-${i}`}>
                  <line
                    x1={tick.x}
                    y1={0}
                    x2={tick.x}
                    y2={innerHeight}
                    className="stroke-gray-200 dark:stroke-gray-700"
                    strokeWidth={1}
                  />
                </g>
              ))}
              {yTicks.map((tick, i) => (
                <g key={`y-grid-${i}`}>
                  <line
                    x1={0}
                    y1={tick.y}
                    x2={innerWidth}
                    y2={tick.y}
                    className="stroke-gray-200 dark:stroke-gray-700"
                    strokeWidth={1}
                  />
                </g>
              ))}
            </>
          )}

          {showArea && (
            <motion.path
              d={areaPath}
              className="fill-primary-100/30 dark:fill-primary-900/30"
              stroke="none"
              initial={animate ? { opacity: 0 } : undefined}
              animate={{ opacity: 1 }}
              transition={{ duration: 1 }}
            />
          )}

          <motion.path
            d={linePath}
            fill="none"
            className="stroke-primary-500 dark:stroke-primary-400"
            strokeWidth={strokeWidth}
            strokeLinejoin="round"
            strokeLinecap="round"
            initial={animate ? { pathLength: 0 } : undefined}
            animate={{ pathLength: 1 }}
            transition={{ duration: 1.5, ease: "easeInOut" }}
          />

          {showDots &&
            data.map((point, i) => (
              <g key={`point-${i}`}>
                <motion.circle
                  cx={scaleX(point.x)}
                  cy={scaleY(point.y)}
                  r={dotSize}
                  className="fill-primary-500 dark:fill-primary-400 stroke-white dark:stroke-gray-900"
                  strokeWidth={2}
                  initial={animate ? { scale: 0 } : undefined}
                  animate={{ scale: 1 }}
                  transition={{ delay: i * 0.05, type: "spring" }}
                  onMouseEnter={() => handlePointHover(point)}
                  onMouseLeave={() => setHoveredPoint(null)}
                  style={{ cursor: "pointer" }}
                />
              </g>
            ))}

          <line
            x1={0}
            y1={innerHeight}
            x2={innerWidth}
            y2={innerHeight}
            className="stroke-gray-300 dark:stroke-gray-600"
            strokeWidth={1.5}
          />
          <line
            x1={0}
            y1={0}
            x2={0}
            y2={innerHeight}
            className="stroke-gray-300 dark:stroke-gray-600"
            strokeWidth={1.5}
          />

          {xTicks.map((tick, i) => (
            <g key={`x-tick-${i}`}>
              <line
                x1={tick.x}
                y1={innerHeight}
                x2={tick.x}
                y2={innerHeight + 6}
                className="stroke-gray-400 dark:stroke-gray-500"
                strokeWidth={1}
              />
              <text
                x={tick.x}
                y={innerHeight + 16}
                textAnchor="middle"
                fontSize={10}
                className="fill-gray-500 dark:fill-gray-400 font-medium"
              >
                {formatXLabel(tick.value)}
              </text>
            </g>
          ))}

          {yTicks.map((tick, i) => (
            <g key={`y-tick-${i}`}>
              <line
                x1={0}
                y1={tick.y}
                x2={-6}
                y2={tick.y}
                className="stroke-gray-400 dark:stroke-gray-500"
                strokeWidth={1}
              />
              <text
                x={-8}
                y={tick.y + 3}
                textAnchor="end"
                fontSize={10}
                className="fill-gray-500 dark:fill-gray-400 font-medium"
              >
                {formatYLabel(tick.value)}
              </text>
            </g>
          ))}

          {xAxisLabel && (
            <text
              x={innerWidth / 2}
              y={innerHeight + 30}
              textAnchor="middle"
              fontSize={12}
              className="fill-gray-700 dark:fill-gray-300 font-semibold"
            >
              {xAxisLabel}
            </text>
          )}
          {yAxisLabel && (
            <text
              x={-margin.left / 2}
              y={innerHeight / 2}
              textAnchor="middle"
              fontSize={12}
              className="fill-gray-700 dark:fill-gray-300 font-semibold"
              transform={`rotate(-90, ${-margin.left / 2}, ${innerHeight / 2})`}
            >
              {yAxisLabel}
            </text>
          )}
        </g>
      </svg>

      {hoveredPoint && (
        <motion.div
          className="absolute z-10 p-2 text-xs md:text-sm bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg dark:shadow-xl pointer-events-none"
          style={{
            left: tooltipPos.x,
            top: tooltipPos.y,
            transform: "translateX(-50%)",
          }}
          initial={{ opacity: 0, y: -10 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -10 }}
          transition={{ type: "spring", damping: 25 }}
        >
          <div className="font-semibold text-primary-600 dark:text-primary-400">
            {hoveredPoint.label || "Data Point"}
          </div>
          <div className="text-gray-700 dark:text-gray-300">
            <span className="font-medium">{xAxisLabel}:</span>{" "}
            {formatXLabel(hoveredPoint.x)}
          </div>
          <div className="text-gray-700 dark:text-gray-300">
            <span className="font-medium">{yAxisLabel}:</span>{" "}
            {formatYLabel(hoveredPoint.y)}
          </div>
        </motion.div>
      )}
    </div>
  );
};

export default LineChart;
