const { useState, useEffect, useRef } = React;

const MetricCard = ({ title, stats }) => (
  <div className="bg-white shadow rounded-lg p-4">
    <div className="pb-2">
      <h2 className="text-lg font-semibold">{title}</h2>
    </div>
    <div className="space-y-2">
      <div className="grid grid-cols-2 gap-2">
        <div>Avg: {stats.avg} ms</div>
        <div>P50: {stats.p50} ms</div>
        <div>P95: {stats.p95} ms</div>
        <div>P99: {stats.p99} ms</div>
      </div>
    </div>
  </div>
);

const DBActivityCard = ({ stats }) => (
  <div className="bg-white shadow rounded-lg p-4">
    <div className="pb-2">
      <h2 className="text-lg font-semibold">Database Activity</h2>
    </div>
    <div className="space-y-2">
      <div className="grid grid-cols-2 gap-2">
        <div>Rows Read: {stats.rowsRead}</div>
        <div>Rows Written: {stats.rowsWritten}</div>
        <div>Total Rows: {stats.totalRows}</div>
      </div>
    </div>
  </div>
);

const CacheActivityCard = ({ stats }) => (
  <div className="bg-white shadow rounded-lg p-4">
    <div className="pb-2">
      <h2 className="text-lg font-semibold">Cache Activity</h2>
    </div>
    <div className="space-y-2">
      <div className="grid grid-cols-2 gap-2">
        <div>Cache Hits: {stats.hits}</div>
        <div>Cache Misses: {stats.misses}</div>
        <div>Hit Rate: {stats.hitRate}%</div>
      </div>
    </div>
  </div>
);

const MetricsDashboard = () => {
  const [data, setData] = useState([]);
  const [timeRange, setTimeRange] = useState("30m");
  const [customStart, setCustomStart] = useState("");
  const [customEnd, setCustomEnd] = useState("");
  const [stats, setStats] = useState({
    service: { avg: 0, p50: 0, p95: 0, p99: 0 },
    db: { avg: 0, p50: 0, p95: 0, p99: 0 },
    cache: { avg: 0, p50: 0, p95: 0, p99: 0 },
    dbActivity: { rowsRead: 0, rowsWritten: 0, totalRows: 0 },
    cacheActivity: { hits: 0, misses: 0, hitRate: 0 },
  });

  const chartRef = useRef(null);
  const chartInstance = useRef(null);

  const getTimeRangeParams = () => {
    const now = Date.now();
    switch (timeRange) {
      case "30m":
        return `?start=${now - 30 * 60 * 1000}&end=${now}`;
      case "1h":
        return `?start=${now - 60 * 60 * 1000}&end=${now}`;
      case "24h":
        return `?start=${now - 24 * 60 * 60 * 1000}&end=${now}`;
      case "7d":
        return `?start=${now - 7 * 24 * 60 * 60 * 1000}&end=${now}`;
      case "custom":
        if (customStart && customEnd) {
          return `?start=${new Date(customStart).getTime()}&end=${new Date(customEnd).getTime()}`;
        }
        return "";
      case "all":
        return "";
      default:
        return `?start=${now - 30 * 60 * 1000}&end=${now}`;
    }
  };

  const bucketDataForChart = (data, bucketSizeMs = 1000) => {
    const buckets = {};

    data.forEach((point) => {
      const bucketKey =
        Math.floor(point.timestamp / bucketSizeMs) * bucketSizeMs;

      if (!buckets[bucketKey]) {
        buckets[bucketKey] = {
          timestamp: bucketKey,
          service_time: [],
          db_time: [],
          cache_time: [],
          db_rows_read: [],
          db_rows_written: [],
          cache_hits: [],
          cache_misses: [],
        };
      }

      buckets[bucketKey].service_time.push(point.service_time);
      buckets[bucketKey].db_time.push(point.db_time);
      buckets[bucketKey].cache_time.push(point.cache_time);
      buckets[bucketKey].db_rows_read.push(point.db_rows_read);
      buckets[bucketKey].db_rows_written.push(point.db_rows_written);
      buckets[bucketKey].cache_hits.push(point.cache_hits);
      buckets[bucketKey].cache_misses.push(point.cache_misses);
    });

    return Object.values(buckets).map((bucket) => ({
      timestamp: bucket.timestamp,
      service_time: _.mean(bucket.service_time),
      db_time: _.mean(bucket.db_time),
      cache_time: _.mean(bucket.cache_time),
      db_rows_read: _.sum(bucket.db_rows_read),
      db_rows_written: _.sum(bucket.db_rows_written),
      cache_hits: _.sum(bucket.cache_hits),
      cache_misses: _.sum(bucket.cache_misses),
    }));
  };

  const calculateStats = (data) => {
    // Input validation with early return
    if (!data?.length) {
      return {
        service: { avg: 0, p50: 0, p95: 0, p99: 0 },
        db: { avg: 0, p50: 0, p95: 0, p99: 0 },
        cache: { avg: 0, p50: 0, p95: 0, p99: 0 },
        dbActivity: { rowsRead: 0, rowsWritten: 0, totalRows: 0 },
        cacheActivity: { hits: 0, misses: 0, hitRate: 0 },
      };
    }

    // Create separate arrays for each metric type and sort them independently
    const serviceValues = data
      .map((d) => Number(d.service_time) || 0)
      .sort((a, b) => a - b);
    const dbValues = data
      .map((d) => Number(d.db_time) || 0)
      .sort((a, b) => a - b);
    const cacheValues = data
      .map((d) => Number(d.cache_time) || 0)
      .sort((a, b) => a - b);

    // Calculate percentile indices
    const len = data.length;
    const p50idx = Math.floor(len * 0.5);
    const p95idx = Math.floor(len * 0.95);
    const p99idx = Math.floor(len * 0.99);

    // Log the actual values we're using
    console.log("Sorted Values Sample:", {
      service: serviceValues.slice(0, 5),
      db: dbValues.slice(0, 5),
      cache: cacheValues.slice(0, 5),
    });

    console.log("Median Values:", {
      service: serviceValues[p50idx],
      db: dbValues[p50idx],
      cache: cacheValues[p50idx],
    });

    // Get latest values for activity metrics
    const latest = data[0] || {
      cache_hits: 0,
      cache_misses: 0,
      db_rows_read: 0,
      db_rows_written: 0,
      db_total_rows: 0,
    };

    const totalCacheRequests = latest.cache_hits + latest.cache_misses;
    const hitRate =
      totalCacheRequests > 0
        ? (latest.cache_hits / totalCacheRequests) * 100
        : 0;

    const stats = {
      service: {
        avg: _.round(_.mean(serviceValues), 2),
        p50: _.round(serviceValues[p50idx] || 0, 2),
        p95: _.round(serviceValues[p95idx] || 0, 2),
        p99: _.round(serviceValues[p99idx] || 0, 2),
      },
      db: {
        avg: _.round(_.mean(dbValues), 2),
        p50: _.round(dbValues[p50idx] || 0, 2),
        p95: _.round(dbValues[p95idx] || 0, 2),
        p99: _.round(dbValues[p99idx] || 0, 2),
      },
      cache: {
        avg: _.round(_.mean(cacheValues), 2),
        p50: _.round(cacheValues[p50idx] || 0, 2),
        p95: _.round(cacheValues[p95idx] || 0, 2),
        p99: _.round(cacheValues[p99idx] || 0, 2),
      },
      dbActivity: {
        rowsRead: latest.db_rows_read,
        rowsWritten: latest.db_rows_written,
        totalRows: latest.db_total_rows,
      },
      cacheActivity: {
        hits: latest.cache_hits,
        misses: latest.cache_misses,
        hitRate: _.round(hitRate, 1),
      },
    };

    // Log final calculated stats
    console.log("Final Stats:", stats);

    return stats;
  };

  const updateChart = (forceReset = false) => {
    if (!chartRef.current) return;

    if (!chartInstance.current || forceReset) {
      if (chartInstance.current) {
        chartInstance.current.destroy();
      }
      initializeChart();
    }

    const bucketedData = bucketDataForChart(data);

    chartInstance.current.data.datasets[0].data = bucketedData.map((d) => ({
      x: d.timestamp,
      y: d.service_time,
    }));
    chartInstance.current.data.datasets[1].data = bucketedData.map((d) => ({
      x: d.timestamp,
      y: d.db_time,
    }));
    chartInstance.current.data.datasets[2].data = bucketedData.map((d) => ({
      x: d.timestamp,
      y: d.cache_time,
    }));

    chartInstance.current.update("none");
  };

  const initializeChart = () => {
    if (!chartRef.current) {
      console.log("Chart ref not ready");
      return;
    }

    console.log("Initializing chart");
    const ctx = chartRef.current.getContext("2d");

    chartInstance.current = new Chart(ctx, {
      type: "line",
      data: {
        datasets: [
          {
            label: "Service Time",
            borderColor: "#8884d8",
            data: [],
            tension: 0.1,
          },
          {
            label: "DB Time",
            borderColor: "#82ca9d",
            data: [],
            tension: 0.1,
          },
          {
            label: "Cache Time",
            borderColor: "#ffc658",
            data: [],
            tension: 0.1,
          },
        ],
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: false,
        scales: {
          x: {
            type: "time",
            time: {
              parser: "MM/DD/YYYY HH:mm",
              tooltipFormat: "ll HH:mm",
              unit: "second",
              displayFormats: {
                second: "HH:mm:ss",
              },
            },
            title: {
              display: true,
              text: "Time",
            },
          },
          y: {
            beginAtZero: true,
            title: {
              display: true,
              text: "Time (ms)",
            },
          },
        },
      },
    });
  };

  const fetchMetrics = async () => {
    try {
      console.log("Fetching metrics with params:", getTimeRangeParams());
      const response = await fetch(`/api/metrics${getTimeRangeParams()}`);
      const newData = await response.json();
      console.log("Received metrics data:", newData);

      if (!newData || newData.length === 0) {
        console.log("No data received");
        setData([]);
        setStats(calculateStats([]));
        return;
      }

      const newStats = calculateStats(newData);
      console.log("Calculated stats:", newStats);
      if (newStats) {
        setStats(newStats);
      }

      setData(newData || []);
    } catch (error) {
      console.error("Error fetching metrics:", error);
      setData([]);
      setStats(calculateStats([]));
    }
  };

  useEffect(() => {
    console.log("Initial fetch and chart setup");
    fetchMetrics();
    updateChart(true);

    let interval;
    if (timeRange !== "all" && timeRange !== "custom") {
      interval = setInterval(fetchMetrics, 1000);
      console.log("Set up polling interval");
    }
    return () => {
      if (interval) {
        console.log("Cleaning up interval");
        clearInterval(interval);
      }
    };
  }, [timeRange, customStart, customEnd]);

  useEffect(() => {
    console.log("Data updated:", data.length, "points");
    if (data.length > 0 && chartRef.current) {
      updateChart();
    }
  }, [data]);

  const exportCSV = () => {
    try {
      console.log("Exporting data:", data);
      const csv = Papa.unparse(data);
      const blob = new Blob([csv], { type: "text/csv" });
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `metrics_export_${new Date().toISOString()}.csv`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      window.URL.revokeObjectURL(url);
    } catch (error) {
      console.error("Error exporting CSV:", error);
    }
  };

  return (
    <div className="container mx-auto p-4 space-y-4">
      <div className="flex justify-between items-center">
        <h1 className="text-2xl font-bold">Service Performance Metrics</h1>
        <div className="flex items-center space-x-4">
          <div className="flex items-center space-x-2">
            <select
              value={timeRange}
              onChange={(e) => setTimeRange(e.target.value)}
              className="rounded border p-2"
            >
              <option value="30m">Last 30m</option>
              <option value="1h">Last 1h</option>
              <option value="24h">Last 24h</option>
              <option value="7d">Last 7d</option>
              <option value="custom">Custom Range</option>
              <option value="all">All Data</option>
            </select>
          </div>

          {timeRange === "custom" && (
            <div className="flex items-center space-x-2">
              <input
                type="datetime-local"
                value={customStart}
                onChange={(e) => setCustomStart(e.target.value)}
                className="rounded border p-2"
              />
              <span>to</span>
              <input
                type="datetime-local"
                value={customEnd}
                onChange={(e) => setCustomEnd(e.target.value)}
                className="rounded border p-2"
              />
            </div>
          )}

          <div className="space-x-2">
            <button
              onClick={exportCSV}
              className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
            >
              Export CSV
            </button>

            <button
              onClick={async () => {
                await fetch("/api/clear", { method: "POST" });
                setData([]);
                if (chartInstance.current) {
                  chartInstance.current.data.datasets.forEach((dataset) => {
                    dataset.data = [];
                  });
                  chartInstance.current.update();
                }
                fetchMetrics();
              }}
              className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
            >
              Clear Data
            </button>
          </div>
        </div>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
        <MetricCard title="Service Time" stats={stats.service} />
        <MetricCard title="Database Time" stats={stats.db} />
        <MetricCard title="Cache Time" stats={stats.cache} />
      </div>

      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        <DBActivityCard stats={stats.dbActivity} />
        <CacheActivityCard stats={stats.cacheActivity} />
      </div>

      <div className="w-full h-96">
        <canvas ref={chartRef}></canvas>
      </div>
    </div>
  );
};

// Render the app
ReactDOM.createRoot(document.getElementById("root")).render(
  <MetricsDashboard />,
);
