from __future__ import annotations

from typing import Any

import numpy as np
import pandas as pd
import plotly.graph_objects as go

from src.config import config
from src.reporting.precompute import precompute_group_distances_and_rings


def _build_current_vs_distance_chart(
	lightning_df: pd.DataFrame,
	dists_km: np.ndarray,
	mask_within: np.ndarray,
	lightning_type_filter: str,
	title: str,
	fig_width: int,
	fig_height: int,
) -> go.Figure | None:
	if lightning_type_filter == "cg":
		type_mask = lightning_df["p_type"].astype(str) == "0"
	else:
		type_mask = lightning_df["p_type"].astype(str) != "0"

	combined_mask = mask_within & type_mask
	if combined_mask.sum() == 0:
		return None

	subset = lightning_df.loc[combined_mask].copy()
	distances = dists_km[combined_mask]
	currents = subset["current"].values.astype(float)

	time_series = pd.to_datetime(subset["local_time"])
	time_dt = time_series.sort_values()

	rings_km = np.array(config.distance_rings, dtype=float) / 1000.0
	ring_colors_cfg = getattr(config, "ring_colors", None) or []
	ring_indices = np.searchsorted(rings_km, distances, side="left").astype(int)
	ring_indices = np.clip(ring_indices, 0, len(rings_km) - 1)

	ring_names: list[str] = []
	for i in range(len(rings_km)):
		if i == 0:
			ring_names.append(f"0-{rings_km[0]:.1f} km")
		else:
			ring_names.append(f"{rings_km[i - 1]:.1f}-{rings_km[i]:.1f} km")

	t_min = time_dt.min()
	t_max = time_dt.max()
	tick_vals = pd.date_range(t_min, t_max, periods=4)
	tick_text = [t.strftime("%d-%m-%Y %H:%M") for t in tick_vals]

	fig = go.Figure()
	for i in range(len(rings_km)):
		mask_ring = ring_indices == i
		if mask_ring.sum() == 0:
			continue
		color = ring_colors_cfg[i] if i < len(ring_colors_cfg) else "gray"
		r_times = time_series.values[mask_ring]
		r_currents = currents[mask_ring]
		r_dists = distances[mask_ring]
		r_time_labels = pd.to_datetime(r_times).strftime("%d-%m-%Y %H:%M").values
		fig.add_trace(
			go.Scatter(
				x=r_times,
				y=r_currents,
				mode="markers",
				name=ring_names[i],
				marker=dict(size=10, opacity=0.8, color=color),
				customdata=np.column_stack((r_dists, r_time_labels)),
				hovertemplate=(
					"Time: %{customdata[1]}<br>"
					"Current: %{y:.0f} A<br>"
					"Distance: %{customdata[0]} km<br>"
					"Ring: " + ring_names[i] + "<br>"
					"<extra></extra>"
				),
			)
		)

	fig.update_layout(
		font=dict(size=16),
		title=dict(text=title, x=0.5, font=dict(size=22)),
		xaxis_title="Time",
		yaxis_title="Current (A)",
		plot_bgcolor="white",
		paper_bgcolor="white",
		xaxis=dict(
			showgrid=True,
			gridcolor="lightgray",
			zeroline=False,
			tickvals=tick_vals,
			ticktext=tick_text,
			tickangle=-25,
			tickfont=dict(size=22),
			title_font=dict(size=28),
		),
		yaxis=dict(
			showgrid=True,
			gridcolor="lightgray",
			zeroline=False,
			tickfont=dict(size=22),
			title_font=dict(size=28),
		),
		legend=dict(
			title="Distance Ring",
			orientation="h",
			x=0.5,
			xanchor="center",
			y=-0.28,
			yanchor="top",
			bgcolor="rgba(255,255,255,0.8)",
			bordercolor="black",
			borderwidth=1,
			font=dict(size=20),
			title_font=dict(size=24),
		),
		width=fig_width,
		height=fig_height,
		margin=dict(l=70, r=40, t=50, b=130),
	)

	return fig


def build_group_lightning_table_data(
	centroid_lat: float, centroid_lng: float, lightning_df: pd.DataFrame
) -> tuple[list[list[str]], list[str]]:
	pre = precompute_group_distances_and_rings(
		centroid_lat, centroid_lng, lightning_df, config.distance_rings
	)
	rows: list[list[str]] = []
	row_colors: list[str] = []
	outermost_km = max(config.distance_rings) / 1000.0
	rings_km = [r / 1000.0 for r in config.distance_rings]

	for i, rec in enumerate(lightning_df.itertuples(index=False)):
		proximity = float(pre["dists_km"][i])
		if proximity > outermost_km:
			continue
		ri = int(pre["ring_idx"][i])
		if ri >= len(rings_km):
			continue
		color = config.ring_colors[ri]
		try:
			from src.utils import format_datetime_ddmmyyyy_hhmmss

			dt_val = (
				rec.local_time
				if not isinstance(rec.local_time, str)
				else pd.to_datetime(str(rec.local_time)[:19])
			)
			local_time = format_datetime_ddmmyyyy_hhmmss(pd.to_datetime(dt_val))
		except Exception:
			local_time = str(getattr(rec, "local_time", ""))[:19]

		lightning_type = "cloud-to-ground" if str(rec.p_type) == "0" else "intercloud"
		height_val = getattr(rec, "ic_height", "")
		if height_val == "":
			height_val = getattr(rec, "height", "")

		rows.append(
			[
				"",
				local_time,
				f"{rec.lat:.5f}",
				f"{rec.lng:.5f}",
				str(rec.current),
				str(height_val),
				lightning_type,
				f"{proximity:.2f}",
			]
		)
		row_colors.append(color)

	sorted_data = sorted(
		zip(rows, row_colors),
		key=lambda x: (0 if x[0][6] == "cloud-to-ground" else 1, float(x[0][7])),
	)
	if sorted_data:
		rows, row_colors = zip(*sorted_data)
		rows = list(rows)
		row_colors = list(row_colors)
		for idx, row in enumerate(rows):
			row[0] = str(idx + 1)
	else:
		rows, row_colors = [], []

	header = [
		"#",
		"Time (Local)",
		"Lat",
		"Lng",
		"Current (amps)",
		"Height (m)",
		"Lightning Type",
		"Proximity (km)",
	]
	return [header] + rows, ["lightgrey"] + row_colors


def build_risk_table_data(
	turbine_df: pd.DataFrame, group_info: dict[str, Any]
) -> tuple[list[list[str]] | None, list[str] | None]:
	if "risk_log" not in turbine_df.columns:
		return None, None

	group_turbines = turbine_df.iloc[group_info["turbine_indices"]]
	rows: list[list[str]] = []
	row_colors: list[str] = []

	from src.utils import get_risk_definition_by_fixed_intervals, get_turbine_color_by_fixed_intervals

	for _, turbine in group_turbines.iterrows():
		risk_log = float(turbine.get("risk_log", 0) or 0)
		color = get_turbine_color_by_fixed_intervals(risk_log)
		rows.append(
			[
				str(turbine.get("name", "N/A")),
				f"{risk_log:.2f}",
				str(get_risk_definition_by_fixed_intervals(risk_log)),
			]
		)
		row_colors.append(str(color))

	sorted_data = sorted(zip(rows, row_colors), key=lambda x: float(x[0][1]), reverse=True)
	if sorted_data:
		rows, row_colors = zip(*sorted_data)
	else:
		rows, row_colors = [], []

	header = ["Turbine Name", "Log Risk", "Risk Definition"]
	return [header] + list(rows), ["lightgrey"] + list(row_colors)

