from __future__ import annotations

from typing import Dict

import pandas as pd

from .flow import sort_for_story
from .formatter import metric_label, percent
from .templates import TEMPLATES


def generate_sentence(insight) -> str:
    data = insight.message_data or {}
    metric = metric_label(insight.name)
    period = _format_period(data.get("period"))

    template_key = _select_template(insight, data)
    template = TEMPLATES.get(template_key, _default_template(insight.type))
    params = _template_params(insight, data, metric, period)

    try:
        return template.format(**params).strip()
    except KeyError:
        return _fallback_sentence(insight, metric, period)


def generate_story(insights):
    output = []
    for insight in sort_for_story(insights):
        sentence = generate_sentence(insight)
        if not sentence:
            continue

        reason = _reason_for_insight(insight)
        if insight.type == "trend" and not reason:
            continue

        output.append({"insight": sentence, "reason": reason})
    return output


def _select_template(insight, data: Dict) -> str:
    if insight.type == "growth":
        return "growth_positive" if insight.score > 0 else "growth_negative"

    if insight.type == "dominance":
        if data.get("position_strength") in {"dominant", "strong"}:
            return "dominance_strong"
        return "dominance_moderate"

    if insight.type == "trend":
        if data.get("trend_stability") == "stable" and data.get("trend_strength") in {
            "strong",
            "moderate",
        }:
            return "trend_consistent"
        if data.get("trend_stability") == "volatile":
            return "trend_volatile"
        return "trend_up" if insight.score > 0 else "trend_down"

    if insight.type == "rate_change":
        baseline = data.get("previous", 0)
        return "rate_rise" if insight.score > baseline else "rate_drop"

    defaults = {
        "growth": "growth_positive" if insight.score > 0 else "growth_negative",
        "dominance": "dominance",
        "rate": "rate",
        "rate_change": (
            "rate_rise" if insight.score > data.get("previous", 0) else "rate_drop"
        ),
        "trend": "trend_up" if insight.score > 0 else "trend_down",
    }
    return defaults.get(insight.type, "growth_positive")


def _default_template(insight_type: str) -> str:
    defaults = {
        "growth": "{metric} changed by {percent}% in {period}",
        "dominance": "{entity} holds {percent}% of {metric}",
        "rate": "{metric} is at {percent}%",
        "rate_change": "{metric} changed from {prev}% to {curr}% in {period}",
        "trend": "{metric} shows a {direction} trend",
    }
    return defaults.get(insight_type, "{metric}: {percent}%")


def _template_params(insight, data: Dict, metric: str, period: str) -> Dict:
    previous_period = _format_period(data.get("previous_period"))
    previous_value = data.get("previous")
    current_value = data.get("current")

    return {
        "metric": metric,
        "percent": percent(abs(insight.score)),
        "period": period,
        "previous_period": previous_period,
        "reason_suffix": _reason_suffix(insight, data),
        "direction": "increase" if insight.score > 0 else "decrease",
        "metric_direction": "growth" if insight.score > 0 else "decline",
        "entity": data.get("entity", "Key entity"),
        "prev": percent(data.get("previous", 0)),
        "curr": percent(data.get("current", insight.score)),
        "previous_value": _format_number(previous_value),
        "current_value": _format_number(current_value),
        "metrics": metric,
        "avg_percent": percent(abs(insight.score)),
        "cause": data.get("primary_cause", "underlying factors"),
        "driver_factor": data.get("primary_driver", "key operational factors"),
        "market_position": data.get("market_position", "strong market presence"),
        "strength": data.get("momentum", "steady"),
        "volatility_desc": data.get("volatility_desc", "stable"),
        "pattern_type": data.get("pattern_type", "cyclical"),
        "typical_behavior": data.get("typical_behavior", "positive performance"),
        "behavior_type": data.get("behavior_type", "performance variation"),
        "key_drivers": data.get("key_drivers", "core business activities"),
        "affected_items": data.get("affected_items", "relevant factors"),
        "efficiency_areas": data.get("efficiency_areas", "operational processes"),
        "performance_improvement": data.get(
            "performance_improvement", "enhanced results"
        ),
        "market_factors": data.get("market_factors", "external conditions"),
        "metric_performance": data.get("metric_performance", "performance metrics"),
        "metric_outcome": data.get("metric_outcome", "observed outcomes"),
        "outperformance_margin": percent(
            data.get("outperformance_margin", abs(insight.score))
        ),
        "rank_position": data.get("rank_position", "favorably"),
        "competitive_advantage": data.get(
            "competitive_advantage", "competitive positioning"
        ),
        "trend_duration": data.get("trend_duration", "sustained"),
        "trend_status": data.get("trend_status", "performing well"),
    }


def _reason_for_insight(insight) -> str:
    if insight.root_causes:
        reason = insight.root_causes[0].strip()
        if not reason:
            return ""
        return reason[0].upper() + reason[1:]
    return ""


def _reason_suffix(insight, data: Dict) -> str:
    parts = []

    if insight.type == "trend":
        momentum = data.get("momentum")
        if momentum:
            parts.append(f"with {momentum} momentum")

    if insight.type == "rate":
        rate_desc = data.get("rate_desc")
        if rate_desc:
            parts.append(f"which is {rate_desc}")

    if not parts:
        return ""
    return f" ({'; '.join(parts)})"


def _fallback_sentence(insight, metric: str, period: str) -> str:
    if insight.type == "growth":
        direction = "increased" if insight.score > 0 else "decreased"
        return f"{metric} {direction} by {percent(abs(insight.score))}% in {period}"
    if insight.type == "dominance":
        return f"Key entity holds {percent(insight.score)}% of {metric}"
    if insight.type == "trend":
        direction = "upward" if insight.score > 0 else "downward"
        return f"{metric} shows {direction} trend"
    return f"{metric}: {percent(insight.score)}% in {period}"


def _format_period(raw_period):
    if raw_period is None or raw_period == "":
        return "the analyzed period"

    parsed = pd.to_datetime(raw_period, errors="coerce")
    if not pd.isna(parsed):
        return parsed.strftime("%B %Y")

    text = str(raw_period).strip()
    if text.isdigit():
        return f"period {text}"
    return text


def _format_number(value) -> str:
    if value is None:
        return "n/a"
    try:
        number = float(value)
    except (TypeError, ValueError):
        return str(value)

    if abs(number) >= 1000:
        return f"{number:,.0f}"
    if number.is_integer():
        return f"{number:.0f}"
    return f"{number:.2f}"
