// Chart.js and date adapter are loaded globally from vendor bundle
// We'll access it later when it's actually loaded

import type { ChartConfiguration, ChartType } from "chart.js";
import type { WeatherChartDataset, YAxisOptions } from "../types/chart-types";
import type { HandleEventFunction } from "../types/events";

// Declare global Chart object
declare global {
  interface Window {
    Chart: typeof Chart;
    chartInstances?: Map<string, ChartHookContext>;
  }
}

// Type for LiveView hooks
interface Hook {
  mounted?: () => void;
  updated?: () => void;
  destroyed?: () => void;
  el: HTMLElement;
  handleEvent: HandleEventFunction;
}

// Define chart hook context type
interface ChartHookContext extends Hook {
  chart?: Chart;
  themeChangeHandler?: () => void;
  renderChart: () => void;
  isDestroyed?: boolean;
  chartLoadRetryTimer?: ReturnType<typeof setTimeout> | null;
  chartLoadRetryCount?: number;
}

// Type for weather history data
interface WeatherHistoryDatum {
  timestamp: string;
  temperature?: number;
  dew_point?: number;
  humidity?: number;
  pressure?: number;
  wind_direction?: number;
  wind_speed?: number;
  wind_gust?: number;
  rain_1h?: number;
  rain_24h?: number;
  rain_since_midnight?: number;
  luminosity?: number;
}

// Type for the event payload
interface UpdateWeatherChartsPayload {
  weather_history: string;
}

// Helper function to safely parse weather history data
const parseWeatherHistory = (
  dataStr: string | undefined,
): WeatherHistoryDatum[] => {
  if (!dataStr) {
    console.warn("No weather history data provided");
    return [];
  }
  try {
    return JSON.parse(dataStr);
  } catch (error) {
    console.error("Failed to parse weather history data:", error);
    return [];
  }
};

// Helper function to get theme-aware colors
const getThemeColors = () => {
  const isDark = document.documentElement.getAttribute("data-theme") === "dark";
  return {
    text: isDark ? "#e5e7eb" : "#111827",
    grid: isDark ? "#374151" : "#9ca3af",
    background: isDark ? "rgba(0, 0, 0, 0.1)" : "rgba(255, 255, 255, 0.1)",
  };
};

// Register a chart instance
const registerChartInstance = (
  element: HTMLElement,
  instance: ChartHookContext,
) => {
  if (!window.chartInstances) {
    window.chartInstances = new Map();
  }
  const elementId = element.id || `chart-${Date.now()}`;
  if (!element.id) element.id = elementId;
  window.chartInstances.set(elementId, instance);
};

// Unregister a chart instance
const unregisterChartInstance = (element: HTMLElement) => {
  if (window.chartInstances && element.id) {
    const instance = window.chartInstances.get(element.id);
    if (instance?.chart) {
      instance.chart.destroy();
    }
    window.chartInstances.delete(element.id);
  }
};

// Get labels from the element
const getLabels = (el: HTMLElement | null): Record<string, string> => {
  if (!el || !el.dataset.labels) return {};
  const raw = el.dataset.labels;
  if (!raw) return {};
  try {
    return JSON.parse(raw);
  } catch {
    return {};
  }
};

// Chart configurations
interface ChartConfig {
  type: ChartType;
  datasets: (
    data: WeatherHistoryDatum[],
    labels: Record<string, string>,
  ) => WeatherChartDataset[];
  title: (labels: Record<string, string>) => string;
  yAxisLabel?: (labels: Record<string, string>) => string;
  yAxisOptions?: YAxisOptions;
}

const chartConfigs: Record<string, ChartConfig> = {
  temperature: {
    type: "line",
    datasets: (data, labels) => [
      {
        label: labels.temp_label || "Temperature (°F)",
        data: data.map((d) => d.temperature),
        borderColor: "red",
        backgroundColor: "rgba(255, 0, 0, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
      {
        label: labels.dew_label || "Dew Point (°F)",
        data: data.map((d) => d.dew_point),
        borderColor: "blue",
        backgroundColor: "rgba(0, 0, 255, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
    ],
    title: (labels) => labels.temp_title || "Temperature & Dew Point (°F)",
    yAxisLabel: (labels) => labels.degf || "°F",
  },
  humidity: {
    type: "line",
    datasets: (data, labels) => [
      {
        label: labels.hum_label || "Humidity (%)",
        data: data.map((d) => d.humidity),
        borderColor: "green",
        backgroundColor: "rgba(0, 255, 0, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
    ],
    title: (labels) => labels.hum_title || "Humidity (%)",
    yAxisLabel: (labels) => labels.percent || "%",
    yAxisOptions: { min: 0, max: 100 },
  },
  pressure: {
    type: "line",
    datasets: (data, labels) => [
      {
        label: labels.prs_label || "Pressure (mb)",
        data: data.map((d) => d.pressure),
        borderColor: "purple",
        backgroundColor: "rgba(128, 0, 128, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
    ],
    title: (labels) => labels.prs_title || "Barometric Pressure (mb)",
    yAxisLabel: (labels) => labels.mb || "mb",
  },
  wind: {
    type: "line",
    datasets: (data, labels) => [
      {
        label: labels.spd_label || "Wind Speed (mph)",
        data: data.map((d) => d.wind_speed),
        borderColor: "orange",
        backgroundColor: "rgba(255, 165, 0, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
      {
        label: labels.gst_label || "Wind Gust (mph)",
        data: data.map((d) => d.wind_gust),
        borderColor: "red",
        backgroundColor: "rgba(255, 0, 0, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
    ],
    title: (labels) => labels.wnd_title || "Wind Speed & Gust (mph)",
    yAxisLabel: (labels) => labels.mph || "mph",
    yAxisOptions: { min: 0 },
  },
  rain: {
    type: "bar",
    datasets: (data, labels) => [
      {
        label: labels.h1_label || "Rain 1h (in)",
        data: data.map((d) => d.rain_1h),
        backgroundColor: "rgba(54, 162, 235, 0.8)",
        borderColor: "rgba(54, 162, 235, 1)",
        borderWidth: 1,
      },
      {
        label: labels.h24_label || "Rain 24h (in)",
        data: data.map((d) => d.rain_24h),
        backgroundColor: "rgba(153, 102, 255, 0.8)",
        borderColor: "rgba(153, 102, 255, 1)",
        borderWidth: 1,
      },
    ],
    title: (labels) => labels.rain_title || "Rainfall (inches)",
    yAxisLabel: (labels) => labels.inches || "inches",
    yAxisOptions: { min: 0 },
  },
  luminosity: {
    type: "line",
    datasets: (data, labels) => [
      {
        label: labels.lum_label || "Luminosity (W/m²)",
        data: data.map((d) => d.luminosity),
        borderColor: "gold",
        backgroundColor: "rgba(255, 215, 0, 0.1)",
        tension: 0.1,
        pointRadius: 0,
        pointHoverRadius: 4,
      },
    ],
    title: (labels) => labels.lum_title || "Solar Radiation (W/m²)",
    yAxisLabel: (labels) => labels.wm2 || "W/m²",
    yAxisOptions: { min: 0 },
  },
};

// Create a chart hook
function createChartHook(configKey: string): Hook {
  const config = chartConfigs[configKey];
  if (!config) {
    throw new Error(`Unknown chart config: ${configKey}`);
  }

  return {
    mounted() {
      const self = this as ChartHookContext;
      self.isDestroyed = false;
      self.chartLoadRetryTimer = null;
      self.chartLoadRetryCount = 0;
      registerChartInstance(self.el, self);
      self.renderChart = () => {
        if (self.isDestroyed || !self.el.isConnected) {
          return;
        }
        if (self.chart) self.chart.destroy();

        const data: WeatherHistoryDatum[] = parseWeatherHistory(
          self.el.dataset.weatherHistory,
        );
        if (data.length === 0) {
          console.debug("No weather data available for chart");
          return;
        }

        // Skip rendering if we have less than 2 data points (can't create a meaningful time series)
        if (data.length < 2) {
          console.debug(
            "Insufficient weather data for chart (need at least 2 data points)",
          );
          return;
        }

        const canvas = self.el.querySelector(
          "canvas",
        ) as HTMLCanvasElement | null;
        if (!canvas) {
          console.error("Canvas element not found for chart");
          return;
        }

        const labels = getLabels(self.el);
        const times = data.map((d) => new Date(d.timestamp));
        const colors = getThemeColors();

        const chartConfig: ChartConfiguration = {
          type: config.type,
          data: {
            labels: times,
            datasets: config.datasets(data, labels),
          },
          options: {
            adapters: { date: { locale: "en-GB" } },
            responsive: true,
            maintainAspectRatio: false,
            interaction: {
              mode: "index",
              intersect: false,
            },
            plugins: {
              title: {
                display: true,
                text: config.title(labels),
                color: colors.text,
              },
              legend: { labels: { color: colors.text } },
              tooltip: {
                enabled: true,
                mode: "index",
                intersect: false,
                callbacks: {
                  label: (context: any) => {
                    const label = context.dataset.label || "";
                    const value = context.parsed.y;
                    if (value === null || value === undefined) return "";
                    return `${label}: ${Number.isInteger(value) ? value : value.toFixed(1)}`;
                  },
                },
              },
            },
            scales: {
              x: {
                type: "time",
                time: {
                  unit: "minute",
                  tooltipFormat: "HH:mm",
                  displayFormats: { minute: "HH:mm", hour: "HH:mm" },
                  locale: "en-GB",
                },
                title: {
                  display: true,
                  text: labels.time || "Time",
                  color: colors.text,
                },
                ticks: { color: colors.text, maxTicksLimit: 8 },
                grid: { color: colors.grid },
              },
              y: {
                title: {
                  display: true,
                  text: config.yAxisLabel ? config.yAxisLabel(labels) : "",
                  color: colors.text,
                },
                ticks: { color: colors.text },
                grid: { color: colors.grid },
                ...(config.yAxisOptions || {}),
              },
            },
          },
        };

        // Check if Chart.js is loaded
        if (!window.Chart) {
          if ((self.chartLoadRetryCount || 0) >= 20) {
            console.error("Chart.js failed to load for weather chart");
            return;
          }

          console.warn("Chart.js not loaded yet, retrying...");
          self.chartLoadRetryCount = (self.chartLoadRetryCount || 0) + 1;
          if (self.chartLoadRetryTimer) {
            clearTimeout(self.chartLoadRetryTimer);
          }
          self.chartLoadRetryTimer = setTimeout(() => {
            self.chartLoadRetryTimer = null;
            self.renderChart();
          }, 100);
          return;
        }

        self.chartLoadRetryCount = 0;
        self.chart = new window.Chart(canvas, chartConfig);
      };

      self.renderChart();
      self.themeChangeHandler = () => self.renderChart();
      window.addEventListener("themeChanged", self.themeChangeHandler);
      self.handleEvent(
        "update_weather_charts",
        ({ weather_history }: UpdateWeatherChartsPayload) => {
          self.el.dataset.weatherHistory = weather_history;
          self.renderChart();
        },
      );
    },

    updated() {
      const self = this as ChartHookContext;
      if (!self.isDestroyed) {
        self.renderChart();
      }
    },

    destroyed() {
      const self = this as ChartHookContext;
      self.isDestroyed = true;
      if (self.chartLoadRetryTimer) {
        clearTimeout(self.chartLoadRetryTimer);
        self.chartLoadRetryTimer = null;
      }
      if (self.themeChangeHandler) {
        window.removeEventListener("themeChanged", self.themeChangeHandler);
      }
      unregisterChartInstance(self.el);
    },
  };
}

// Export weather chart hooks
export const WeatherChartHooks: Record<string, Hook> = {
  ChartJSTempChart: createChartHook("temperature"),
  ChartJSHumidityChart: createChartHook("humidity"),
  ChartJSPressureChart: createChartHook("pressure"),
  ChartJSWindChart: createChartHook("wind"),
  ChartJSRainChart: createChartHook("rain"),
  ChartJSLuminosityChart: createChartHook("luminosity"),
};

export default WeatherChartHooks;
