from __future__ import annotations

import re
import unicodedata
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from typing import Optional
from zoneinfo import ZoneInfo

import pandas as pd


_DD_MM_YYYY_RE = re.compile(r"^\d{2}-\d{2}-\d{4}$")


def slugify_ascii_underscore(value: str) -> str:
	"""
	Convert `value` into an ASCII-only slug suitable for filenames.

	Rules:
	- spaces -> underscore
	- keep [A-Za-z0-9._-], convert everything else to underscore
	- collapse consecutive underscores
	- trim leading/trailing underscores
	"""
	if value is None:
		return "report"

	s = str(value).strip()
	if not s:
		return "report"

	s = s.replace(" ", "_")
	# Remove diacritics while preserving base ASCII letters (e.g. 'ğ' -> 'g').
	s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode("ascii")
	s = re.sub(r"[^A-Za-z0-9._-]+", "_", s)
	s = re.sub(r"_+", "_", s).strip("_")
	return s or "report"


@dataclass(frozen=True)
class FarmLocalDateRange:
	start_date_yyyy_mm_dd: str
	end_date_yyyy_mm_dd: str


def _parse_date_value_local(value: str, tz: Optional[ZoneInfo]) -> date:
	"""
	Parse a date-like string from config into a local `date` in the given timezone.

	Config supported formats:
	- DD-MM-YYYY (treated as already-local calendar date)
	- ISO datetime (converted to tz, then local calendar date extracted)
	"""
	v = str(value).strip()

	if _DD_MM_YYYY_RE.match(v):
		# Local calendar date, no timezone conversion needed.
		dt = datetime.strptime(v, "%d-%m-%Y")
		return dt.date()

	# ISO datetime path: interpret as UTC and convert to target timezone.
	# `utc=True` yields tz-aware timestamps.
	ts = pd.to_datetime(v, utc=True, errors="raise")
	if tz:
		ts = ts.tz_convert(tz)
	return ts.date()


def farm_local_date_range_from_config(farm: dict) -> FarmLocalDateRange:
	"""
	Compute (start_date, end_date) for filename naming from `wind_farms_config.json`,
	using the farm timezone and interpreting configured date values as local.
	"""
	tz_name = farm.get("report_config", {}).get("timezone")
	tz = ZoneInfo(tz_name) if tz_name else None

	date_range_cfg = farm.get("api_params", {}).get("date_range", {})
	method = str(date_range_cfg.get("method", "auto")).lower()

	if method == "manual":
		start_val = date_range_cfg.get("start_date")
		end_val = date_range_cfg.get("end_date")
		if not start_val or not end_val:
			raise ValueError("Manual date_range requires start_date and end_date")
		start_dt = _parse_date_value_local(str(start_val), tz)
		end_dt = _parse_date_value_local(str(end_val), tz)
		return FarmLocalDateRange(
			start_date_yyyy_mm_dd=start_dt.strftime("%Y-%m-%d"),
			end_date_yyyy_mm_dd=end_dt.strftime("%Y-%m-%d"),
		)

	# Auto mode: compute a local date range based on `query_range.method`.
	query_range_cfg = date_range_cfg.get("query_range", {}) or {}
	query_method = str(query_range_cfg.get("method", "current_month")).lower()

	now_local = datetime.now(tz).date() if tz else datetime.now().date()

	if query_method == "current_month":
		start_dt = date(now_local.year, now_local.month, 1)
		end_dt = now_local
	elif query_method == "last_month":
		if now_local.month == 1:
			prev_year = now_local.year - 1
			prev_month = 12
		else:
			prev_year = now_local.year
			prev_month = now_local.month - 1
		start_dt = date(prev_year, prev_month, 1)
		# Last day of previous month:
		first_of_next_month = date(prev_year, prev_month, 1) + timedelta(days=32)
		end_dt = date(first_of_next_month.year, first_of_next_month.month, 1) - timedelta(days=1)
	elif query_method == "days_back":
		days = int(query_range_cfg.get("days", 30))
		start_dt = now_local - timedelta(days=days)
		end_dt = now_local
	elif query_method == "custom":
		start_val = query_range_cfg.get("start_date")
		end_val = query_range_cfg.get("end_date")
		if not start_val or not end_val:
			raise ValueError("Auto date_range.custom requires query_range.start_date and end_date")
		start_dt = _parse_date_value_local(str(start_val), tz)
		end_dt = _parse_date_value_local(str(end_val), tz)
	else:
		# Fallback: treat as current_month.
		start_dt = date(now_local.year, now_local.month, 1)
		end_dt = now_local

	return FarmLocalDateRange(
		start_date_yyyy_mm_dd=start_dt.strftime("%Y-%m-%d"),
		end_date_yyyy_mm_dd=end_dt.strftime("%Y-%m-%d"),
	)

