"""
Human Light Reply — варіативні відповіді для Light mode Степана.

Без LLM. Без рефакторингу архітектури.

Seeded randomness: стабільна варіативність на основі sha256(user_id + current_day).
  - Стабільна в межах одного дня (не "скаче" між повідомленнями).
  - Змінюється щодня (не "скриптова" через місяць).

Типи light-подій:
  greeting        — "привіт", "добрий ранок", …
  thanks          — "дякую", "спасибі", …
  ack             — "ок", "зрозумів", "добре", "чудово", …
  short_followup  — ≤6 слів, є last_topic, немає action verbs
  weather_followup — "а якщо дощ?", "мороз", "вітер" + last_topic + FarmProfile

Greeting без теми: 3 режими залежно від interaction_count:
  neutral (count 0–2):     "На звʼязку." / "Слухаю."
  soft (count 3–7):        "Що сьогодні рухаємо?"
  contextual (count 8+):  "По плануванню чи по датчиках?"

Правила:
  - Якщо є name → звертатись по імені (1 раз на greeting)
  - Якщо є last_topic → підхоплення теми на greeting / short_followup
  - На thanks/ack → 2–6 слів, без питань
  - Одне питання максимум, вибір з двох (без слова "оберіть")
  - Заборонено: "чим допомогти", шаблонні вступи, запуск систем, згадки помилок

Fail-safe: будь-який виняток → None (fallback до LLM).
"""

from __future__ import annotations

import hashlib
import logging
import random
import re
from datetime import date

logger = logging.getLogger(__name__)

# ─── Topic label map ─────────────────────────────────────────────────────────

_TOPIC_LABELS: dict[str, str] = {
    "plan_day":              "план на день",
    "plan_week":             "план на тиждень",
    "plan_vs_fact":          "план/факт",
    "show_critical_tomorrow": "критичні задачі на завтра",
    "close_plan":            "закриття плану",
    "iot_status":            "стан датчиків",
    "general":               "попереднє питання",
}

def _topic_label(last_topic: str | None) -> str:
    if not last_topic:
        return "попередню тему"
    return _TOPIC_LABELS.get(last_topic, last_topic.replace("_", " "))


# ─── Phrase banks ─────────────────────────────────────────────────────────────

_GREETING_WITH_TOPIC: list[str] = [
    "Привіт{name}. По {topic} є оновлення, чи рухаємось за планом?",
    "Привіт{name}. {topic_cap} — ще актуально чи є нова задача?",
    "Привіт{name}. Продовжуємо з {topic}, чи щось змінилось?",
    "Привіт{name}. Що по {topic} — є нові дані?",
    "Привіт{name}. По {topic} все гаразд чи треба щось уточнити?",
    "Привіт{name}. {topic_cap} — рухаємось далі чи є зміни?",
]

# Greeting без теми — 3 рівні природності залежно від interaction_count

# Рівень 0–2 (новий або рідко спілкується): нейтральний, без питань
_GREETING_NEUTRAL: list[str] = [
    "На звʼязку{name}.",
    "Слухаю{name}.",
    "Привіт{name}.",
    "Так{name}?",
]

# Рівень 3–7 (починає звикати): м'який відкритий промпт
_GREETING_SOFT: list[str] = [
    "Привіт{name}. Що сьогодні рухаємо?",
    "Привіт{name}. З чого починаємо?",
    "Привіт{name}. Є нова задача?",
    "Привіт{name}. Що по плану?",
    "Привіт{name}. Що маємо сьогодні?",
]

# Рівень 8+ (знайомий): контекстна здогадка
_GREETING_CONTEXTUAL: list[str] = [
    "Привіт{name}. По плануванню чи по датчиках?",
    "Привіт{name}. Операції чи аналітика?",
    "Привіт{name}. Польові чи офісні питання?",
    "Привіт{name}. Що сьогодні — план чи факт?",
]

_THANKS: list[str] = [
    "Прийняв.",
    "Добре.",
    "Зрозумів.",
    "Ок.",
    "Домовились.",
    "Тримаю в курсі.",
    "Прийнято.",
    "Зафіксував.",
]

_ACK: list[str] = [
    "Ок, продовжуємо.",
    "Прийнято.",
    "Зрозумів.",
    "Добре.",
    "Ок.",
    "Зафіксував.",
    "Чітко.",
    "Прийняв.",
]

_SHORT_FOLLOWUP_WITH_TOPIC: list[str] = [
    "По {topic} — {text_frag}",
    "Щодо {topic}: {text_frag}",
    "Так, по {topic} — {text_frag}",
    "По {topic} є деталі. {text_frag}",
    "Стосовно {topic}: {text_frag}",
]

_OFFTOPIC: list[str] = [
    "Я можу допомогти з роботами або даними ферми. Що саме потрібно зробити?",
    "Це не моя ділянка. Щодо ферми або операцій — скажи, що треба.",
    "Готовий допомогти з польовими операціями або аналітикою. Що конкретно?",
    "Моя область — агровиробництво і дані ферми. Скажи, що потрібно.",
]

# ─── Weather mini-knowledge ───────────────────────────────────────────────────

_WEATHER_RE = re.compile(
    r'\b(дощ|злива|мороз|заморозк|вітер|спека|суша|туман|град|сніг|опади)\w*\b',
    re.IGNORECASE | re.UNICODE,
)

# rule-based відповіді: (weather_word_stem, phase_hint) → reply
# Досить 5–7 найчастіших випадків.
_WEATHER_RULES: list[tuple[str, str | None, str]] = [
    # (тригер-підрядок, фаза або None, відповідь)
    ("дощ",      "growing",    "Якщо дощ — переносимо обробку на вікно після висихання ґрунту (зазвичай 1–2 доби)."),
    ("дощ",      "sowing",     "Дощ під час сівби: зупиняємо якщо злива, продовжуємо при легкому."),
    ("дощ",      None,         "Якщо дощ — обробка відкладається. Уточни фазу?"),
    ("злива",    None,         "Злива — зупиняємо польові роботи до стабілізації."),
    ("мороз",    "growing",    "Заморозки в фазу вегетації — критично. Перевір поріг чутливості культури."),
    ("мороз",    "sowing",     "Мороз під час сівби — призупиняємо. Насіння не проростає нижче +5°C."),
    ("мороз",    None,         "При морозі — польові роботи під питанням. Яка культура?"),
    ("спека",    "growing",    "Спека понад 35°C — збільш полив якщо є зрошення, контролюй IoT."),
    ("вітер",    None,         "Сильний вітер — обприскування не проводимо."),
    ("суша",     "growing",    "Суха погода в вегетацію — пріоритет зрошення."),
    ("заморозк", None,         "Заморозки — перевір чутливість культури і стан плівки/укриття."),
]


_ZZR_RE = re.compile(
    r'\b(обробк|обприскування|гербіцид|фунгіцид|ЗЗР|пестицид|інсектицид|протруювач)\w*\b',
    re.IGNORECASE | re.UNICODE,
)

_ZZR_DISCLAIMER = " Дозування та вікна застосування — за етикеткою препарату та регламентом."


def _weather_reply(text: str, farm_profile: dict | None) -> str | None:
    """
    Повертає коротку правильну відповідь якщо текст містить погодний тригер.
    Враховує FarmProfile.season_state якщо доступний.
    Якщо текст також містить ЗЗР-тригери — додає застереження про регламент.
    Повертає None якщо погодного тригера немає.
    """
    if not _WEATHER_RE.search(text):
        return None
    tl = text.lower()
    phase = (farm_profile or {}).get("season_state") or (farm_profile or {}).get("seasonal_context", {}).get("current_phase")
    has_zzr = bool(_ZZR_RE.search(text))
    for trigger, rule_phase, reply in _WEATHER_RULES:
        if trigger in tl:
            if rule_phase is None or rule_phase == phase:
                return reply + (_ZZR_DISCLAIMER if has_zzr else "")
    return None


# ─── Seeded RNG ───────────────────────────────────────────────────────────────

def _seeded_rng(user_id: str | None, day: str | None = None) -> random.Random:
    """
    Повертає Random зі стабільним seed на основі sha256(user_id + current_day).
    Стабільний в межах дня — інший завтра.
    sha256 замість hash() — бо builtin hash() солиться per-process.
    """
    if not user_id:
        return random.Random(42)
    today = day or date.today().isoformat()
    raw = f"{user_id}:{today}"
    seed_int = int(hashlib.sha256(raw.encode()).hexdigest(), 16) % (2**32)
    return random.Random(seed_int)


def _pick(rng: random.Random, options: list[str]) -> str:
    return rng.choice(options)


# ─── Event classifiers ────────────────────────────────────────────────────────

_GREETING_RE = re.compile(
    r'^(привіт|добрий\s+\w+|доброго\s+\w+|hello|hi|hey|вітаю|вітання|hey stepan|привітання)[\W]*$',
    re.IGNORECASE | re.UNICODE,
)
_THANKS_RE = re.compile(
    r'^(дякую|дякуй|спасибі|дякую степан|велике дякую|щиро дякую)[\W]*$',
    re.IGNORECASE | re.UNICODE,
)
_ACK_RE = re.compile(
    r'^(ок|окей|добре|зрозумів|зрозуміла|ясно|зрозуміло|чудово|супер|ага|угу|так|о[кк])[\W]*$',
    re.IGNORECASE | re.UNICODE,
)
_ACTION_VERB_RE = re.compile(
    r'\b(зроби|перевір|порахуй|підготуй|онови|створи|запиши|зафіксуй|внеси'
    r'|проаналізуй|порівняй|розрахуй|сплануй|покажи|заплануй|закрий|відкрий)\b',
    re.IGNORECASE | re.UNICODE,
)
_URGENT_RE = re.compile(
    r'\b(аварія|терміново|критично|тривога|невідкладно|alert|alarm|critical)\b',
    re.IGNORECASE | re.UNICODE,
)


def _is_short_followup(text: str, last_topic: str | None) -> bool:
    """Коротка репліка (≤6 слів) з last_topic і без action verbs → light follow-up."""
    words = text.strip().split()
    if len(words) > 6:
        return False
    if last_topic is None:
        return False
    if _ACTION_VERB_RE.search(text):
        return False
    if _URGENT_RE.search(text):
        return False
    return True


# ─── Main API ─────────────────────────────────────────────────────────────────

def classify_light_event(text: str, last_topic: str | None) -> str | None:
    """
    Класифікує текст у тип light-події.
    Повертає: 'greeting' | 'thanks' | 'ack' | 'short_followup' | 'offtopic' | None
    None → not a clear light event (caller should use LLM path)
    """
    t = text.strip()
    if _GREETING_RE.match(t):
        return "greeting"
    if _THANKS_RE.match(t):
        return "thanks"
    if _ACK_RE.match(t):
        return "ack"
    if _is_short_followup(t, last_topic):
        return "short_followup"
    return None


def _pick_recent_label(rng: random.Random, user_profile: dict) -> str | None:
    """
    З ймовірністю 0.2 (seeded) повертає тему recent_topics[-2] замість останньої.
    Це дає відчуття що Степан пам'ятає більше ніж 1 тему, але не нав'язливо.
    Ніколи не повертає дві теми одразу.
    """
    topics = user_profile.get("recent_topics", [])
    if len(topics) < 2:
        return user_profile.get("last_topic_label") or _topic_label(user_profile.get("last_topic"))
    # Use seeded rng: low probability (≈20%) to pick the second-to-last topic
    if rng.random() < 0.2:
        entry = topics[-2]
        return entry.get("label") or _topic_label(entry.get("intent"))
    last = topics[-1]
    return last.get("label") or _topic_label(last.get("intent"))


def build_light_reply(
    text: str,
    user_profile: dict | None,
    farm_profile: dict | None = None,
    light_event: str | None = None,
) -> str | None:
    """
    Будує детерміновану (seeded) відповідь для Light mode без LLM.

    Повертає:
      str  — готова відповідь (тоді LLM не потрібен)
      None — не підходить для без-LLM відповіді, треба LLM path

    Fail-safe: виняток → None (fallback до LLM).
    """
    try:
        up = user_profile or {}
        user_id = up.get("user_id") or ""
        last_topic = up.get("last_topic")
        name = up.get("name") or ""
        name_suffix = f", {name}" if name else ""
        interaction_count = up.get("interaction_count", 0)

        rng = _seeded_rng(user_id)  # daily seed: changes each day

        if light_event is None:
            light_event = classify_light_event(text, last_topic)

        # ── Weather follow-up (priority before general short_followup) ─────────
        if light_event == "short_followup":
            weather = _weather_reply(text, farm_profile)
            if weather:
                return weather

        # ── Greeting ──────────────────────────────────────────────────────────
        if light_event == "greeting":
            if last_topic:
                # Use human label if available (last_topic_label), else fallback to intent label
                topic = up.get("last_topic_label") or _topic_label(last_topic)
                # Contextual experienced users: occasionally recall second-last topic
                if interaction_count >= 8:
                    topic = _pick_recent_label(rng, up) or topic
                template = _pick(rng, _GREETING_WITH_TOPIC)
                return template.format(
                    name=name_suffix,
                    topic=topic,
                    topic_cap=topic[:1].upper() + topic[1:] if topic else topic,
                    text_frag="",
                ).rstrip()
            else:
                # 3 levels based on how well Stepan knows this user
                if interaction_count <= 2:
                    template = _pick(rng, _GREETING_NEUTRAL)
                elif interaction_count <= 7:
                    template = _pick(rng, _GREETING_SOFT)
                else:
                    template = _pick(rng, _GREETING_CONTEXTUAL)
                return template.format(name=name_suffix)

        # ── Thanks ────────────────────────────────────────────────────────────
        if light_event == "thanks":
            return _pick(rng, _THANKS)

        # ── Ack ───────────────────────────────────────────────────────────────
        if light_event == "ack":
            return _pick(rng, _ACK)

        # ── Short follow-up (no weather trigger) ──────────────────────────────
        if light_event == "short_followup" and last_topic:
            # Prefer human label over raw intent key
            topic = up.get("last_topic_label") or _topic_label(last_topic)
            text_frag = text.strip().rstrip("?").strip()
            template = _pick(rng, _SHORT_FOLLOWUP_WITH_TOPIC)
            return template.format(topic=topic, text_frag=text_frag)

        return None  # Let LLM handle it

    except Exception as exc:
        logger.warning("light_reply.build_light_reply error: %s", exc)
        return None
