"""
tests/test_stepan_v4_farm_state.py

v4 Farm State Layer:
  - detect_farm_state_updates (crop, stage, issue, risk)
  - update_farm_state (session merge, dedup, TTL)
  - build_farm_state_prefix (format, empty cases, TTL)
  - Isolation: prefix NOT added in doc/web mode
  - State persists within session
"""
import time
import pytest

from crews.agromatrix_crew.farm_state import (
    detect_farm_state_updates,
    update_farm_state,
    build_farm_state_prefix,
    FARM_STATE_TTL,
)


# ─── 1. detect_farm_state_updates ────────────────────────────────────────────

class TestDetectFarmStateUpdates:

    def test_crop_kukurudza(self):
        u = detect_farm_state_updates("По кукурудзі що робити далі?")
        assert u.get("current_crop") == "кукурудза"

    def test_crop_inflection_kukurudzu(self):
        u = detect_farm_state_updates("Переглянь стан кукурудзу на полі")
        assert u.get("current_crop") == "кукурудза"

    def test_crop_wheat(self):
        u = detect_farm_state_updates("Пшениця виглядає кволою")
        assert u.get("current_crop") == "пшениця"

    def test_crop_sunflower(self):
        u = detect_farm_state_updates("соняшник V6 посуха")
        assert u.get("current_crop") == "соняшник"

    def test_crop_rapeseed(self):
        u = detect_farm_state_updates("ріпак — йде цвітіння")
        assert u.get("current_crop") == "ріпак"

    def test_stage_v6(self):
        u = detect_farm_state_updates("Стадія V6, є жовтизна")
        assert u.get("growth_stage") == "V6"

    def test_stage_kushennya(self):
        u = detect_farm_state_updates("пшениця, фаза кущення")
        assert "кущення" in u.get("growth_stage", "").lower()

    def test_stage_flowering(self):
        u = detect_farm_state_updates("ріпак іде на цвітіння")
        assert "цвітіння" in u.get("growth_stage", "").lower()

    def test_issue_zhovtyzna(self):
        u = detect_farm_state_updates("є жовтизна на листі")
        assert "жовтизна" in u.get("recent_issue", "").lower()

    def test_issue_deficit(self):
        u = detect_farm_state_updates("дефіцит азоту у посівах")
        assert "дефіцит" in u.get("recent_issue", "").lower()

    def test_issue_shkidnyk(self):
        u = detect_farm_state_updates("з'явились шкідники на кукурудзі")
        assert "шкідник" in u.get("recent_issue", "").lower()

    def test_risk_posuxa(self):
        u = detect_farm_state_updates("на полі посуха вже 2 тижні")
        assert "посуха" in u.get("risk_flags", [])

    def test_risk_zamorozok(self):
        u = detect_farm_state_updates("очікується заморозок вночі")
        assert any("заморозок" in r for r in u.get("risk_flags", []))

    def test_multiple_fields(self):
        u = detect_farm_state_updates("кукурудза V6, дефіцит азоту, посуха")
        assert u.get("current_crop") == "кукурудза"
        assert "V6" in u.get("growth_stage", "")
        assert "дефіцит" in u.get("recent_issue", "").lower()
        assert any("посуха" in r for r in u.get("risk_flags", []))

    def test_empty_text(self):
        u = detect_farm_state_updates("")
        assert u == {}

    def test_no_match(self):
        u = detect_farm_state_updates("привіт, як справи сьогодні?")
        assert u == {}

    def test_fail_safe_garbage(self):
        u = detect_farm_state_updates(None)  # type: ignore
        assert u == {}


# ─── 2. update_farm_state ─────────────────────────────────────────────────────

class TestUpdateFarmState:

    def test_creates_farm_state(self):
        session = {}
        update_farm_state(session, {"current_crop": "кукурудза"})
        assert session["farm_state"]["current_crop"] == "кукурудза"

    def test_sets_last_update_ts(self):
        now = time.time()
        session = {}
        update_farm_state(session, {"current_crop": "соняшник"}, now_ts=now)
        assert abs(session["farm_state"]["last_update_ts"] - now) < 1

    def test_updates_existing(self):
        session = {"farm_state": {"current_crop": "пшениця", "last_update_ts": 0.0}}
        update_farm_state(session, {"current_crop": "кукурудза"})
        assert session["farm_state"]["current_crop"] == "кукурудза"

    def test_merges_risk_flags(self):
        session = {"farm_state": {"risk_flags": ["посуха"], "last_update_ts": 0.0}}
        update_farm_state(session, {"risk_flags": ["заморозок"]})
        flags = session["farm_state"]["risk_flags"]
        assert "посуха" in flags
        assert "заморозок" in flags

    def test_dedup_risk_flags(self):
        session = {"farm_state": {"risk_flags": ["посуха", "спека"], "last_update_ts": 0.0}}
        update_farm_state(session, {"risk_flags": ["посуха"]})
        flags = session["farm_state"]["risk_flags"]
        assert flags.count("посуха") == 1

    def test_risk_flags_max_5(self):
        session = {"farm_state": {"risk_flags": ["r1", "r2", "r3", "r4"], "last_update_ts": 0.0}}
        update_farm_state(session, {"risk_flags": ["r5", "r6"]})
        assert len(session["farm_state"]["risk_flags"]) <= 5

    def test_empty_updates_noop(self):
        session = {"farm_state": {"current_crop": "ріпак", "last_update_ts": 0.0}}
        update_farm_state(session, {})
        assert session["farm_state"]["current_crop"] == "ріпак"

    def test_fail_safe_exception(self):
        # session = None → fail-safe, no exception
        update_farm_state(None, {"current_crop": "соя"})  # type: ignore


# ─── 3. build_farm_state_prefix ──────────────────────────────────────────────

class TestBuildFarmStatePrefix:

    def _session_with_state(self, now: float, **kwargs) -> dict:
        fs = {"last_update_ts": now}
        fs.update(kwargs)
        return {"farm_state": fs}

    def test_empty_if_no_crop(self):
        now = time.time()
        s = self._session_with_state(now, growth_stage="V6")
        assert build_farm_state_prefix(s) == ""

    def test_basic_prefix(self):
        now = time.time()
        s = self._session_with_state(now, current_crop="кукурудза")
        p = build_farm_state_prefix(s)
        assert "кукурудза" in p
        assert "[Контекст господарства]" in p

    def test_includes_stage(self):
        now = time.time()
        s = self._session_with_state(now, current_crop="кукурудза", growth_stage="V6")
        p = build_farm_state_prefix(s)
        assert "V6" in p
        assert "Стадія" in p

    def test_includes_issue(self):
        now = time.time()
        s = self._session_with_state(now, current_crop="кукурудза", recent_issue="жовтизна")
        p = build_farm_state_prefix(s)
        assert "жовтизна" in p
        assert "Проблема" in p

    def test_includes_risks(self):
        now = time.time()
        s = self._session_with_state(now, current_crop="соняшник", risk_flags=["посуха", "спека"])
        p = build_farm_state_prefix(s)
        assert "посуха" in p
        assert "Ризики" in p

    def test_max_lines(self):
        now = time.time()
        s = self._session_with_state(
            now,
            current_crop="кукурудза",
            growth_stage="V6",
            recent_issue="жовтизна",
            risk_flags=["посуха", "заморозок"],
        )
        p = build_farm_state_prefix(s)
        assert len(p.splitlines()) <= 5

    def test_empty_if_ttl_expired(self):
        old_ts = time.time() - FARM_STATE_TTL - 10
        s = self._session_with_state(old_ts, current_crop="кукурудза")
        assert build_farm_state_prefix(s) == ""

    def test_empty_if_no_farm_state(self):
        assert build_farm_state_prefix({}) == ""

    def test_fail_safe(self):
        assert build_farm_state_prefix(None) == ""  # type: ignore


# ─── 4. State persists within session ────────────────────────────────────────

class TestFarmStatePersistence:

    def test_crop_persists_across_messages(self):
        session = {}
        # Перше повідомлення — встановлює crop
        u1 = detect_farm_state_updates("По кукурудзі є дефіцит азоту")
        update_farm_state(session, u1)
        # Друге повідомлення — без crop, але є farm_state
        u2 = detect_farm_state_updates("Що робити?")
        update_farm_state(session, u2)
        # Стан зберігся
        assert session["farm_state"]["current_crop"] == "кукурудза"

    def test_issue_updates_on_new_message(self):
        session = {}
        update_farm_state(session, {"current_crop": "пшениця", "recent_issue": "жовтизна"})
        update_farm_state(session, {"recent_issue": "іржа"})
        assert session["farm_state"]["recent_issue"] == "іржа"

    def test_stage_updates(self):
        session = {}
        update_farm_state(session, {"current_crop": "кукурудза", "growth_stage": "V4"})
        update_farm_state(session, {"growth_stage": "V6"})
        assert session["farm_state"]["growth_stage"] == "V6"


# ─── 5. Ізоляція: prefix NOT в doc / web mode ─────────────────────────────────

def _would_inject(context_mode: str, domain: str, session: dict) -> bool:
    """Симуляція умови ін'єкції з run.py."""
    if context_mode == "doc" or domain == "web":
        return False
    return bool(build_farm_state_prefix(session))


class TestFarmStatePrefixIsolation:

    def _fresh_session(self) -> dict:
        now = time.time()
        session = {}
        update_farm_state(session, {"current_crop": "кукурудза"}, now_ts=now)
        return session

    def test_not_injected_in_doc_mode(self):
        s = self._fresh_session()
        assert not _would_inject("doc", "doc", s)

    def test_not_injected_in_web_domain(self):
        s = self._fresh_session()
        assert not _would_inject("general", "web", s)

    def test_injected_in_general_mode(self):
        s = self._fresh_session()
        assert _would_inject("general", "general", s)

    def test_injected_in_vision_domain(self):
        s = self._fresh_session()
        assert _would_inject("general", "vision", s)

    def test_not_injected_if_no_crop(self):
        s = {"farm_state": {"growth_stage": "V6", "last_update_ts": time.time()}}
        assert not _would_inject("general", "general", s)

    def test_not_injected_if_ttl_expired(self):
        old_ts = time.time() - FARM_STATE_TTL - 60
        s = {"farm_state": {"current_crop": "кукурудза", "last_update_ts": old_ts}}
        assert not _would_inject("general", "general", s)


# ─── 6. FARM_STATE_TTL constant ──────────────────────────────────────────────

class TestConstants:
    def test_farm_state_ttl_30min(self):
        assert FARM_STATE_TTL == 1800.0
