from __future__ import annotations

import importlib.util
import json
import sys
import tempfile
import unittest
from decimal import Decimal
from pathlib import Path

REPO_DIR = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location(
    "consumption_mode", REPO_DIR / "scripts/consumption_mode.py"
)
cm = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
sys.modules[SPEC.name] = cm
SPEC.loader.exec_module(cm)


def restricted(allowance: str = "45.83") -> dict:
    return {
        "schema_version": "0.1",
        "record_type": "consumption_mode",
        "financial_entity_id": "entity:coulomb",
        "period": "2026-09",
        "consumption_mode": "restricted",
        "new_transfer_charges_allowed_eur": allowance,
        "terms_version": "0.1",
    }


class ConsumptionModeTests(unittest.TestCase):
    def test_open_entity_unchanged(self) -> None:
        signal = restricted()
        signal["consumption_mode"] = "open"
        signal["new_transfer_charges_allowed_eur"] = None
        result = cm.decide(
            entity_id="entity:coulomb",
            order_class="new-order",
            estimate_eur=Decimal("200.00"),
            signal=signal,
        )
        self.assertEqual(result["decision"], "admit")
        self.assertEqual(result["reason"], "open")

    def test_restricted_cannot_exceed_allowance(self) -> None:
        result = cm.decide(
            entity_id="entity:coulomb",
            order_class="new-order",
            estimate_eur=Decimal("45.84"),
            signal=restricted(),
        )
        self.assertEqual(result["decision"], "refuse")
        self.assertEqual(result["reason"], "exceeds-allowance")

    def test_restricted_within_allowance(self) -> None:
        result = cm.decide(
            entity_id="entity:coulomb",
            order_class="new-order",
            estimate_eur=Decimal("45.83"),
            signal=restricted(),
        )
        self.assertEqual(result["decision"], "admit")
        self.assertEqual(result["reason"], "within-allowance")

    def test_restricted_without_estimate_refused(self) -> None:
        result = cm.decide(
            entity_id="entity:coulomb",
            order_class="elastic",
            estimate_eur=None,
            signal=restricted(),
        )
        self.assertEqual(result["decision"], "refuse")
        self.assertEqual(result["reason"], "restricted-estimate-required")

    def test_safety_admitted_as_exception(self) -> None:
        result = cm.decide(
            entity_id="entity:coulomb",
            order_class="safety",
            estimate_eur=Decimal("200.00"),
            signal=restricted(),
        )
        self.assertEqual(result["decision"], "admit")
        self.assertTrue(result["exception"])

    def test_no_signal_is_not_restricted(self) -> None:
        result = cm.decide(
            entity_id="entity:coulomb",
            order_class="new-order",
            estimate_eur=Decimal("999.00"),
            signal=None,
        )
        self.assertEqual(result["decision"], "admit")
        self.assertEqual(result["reason"], "no-signal-not-restricted")

    def test_railiance_self_use_always_open(self) -> None:
        result = cm.decide(
            entity_id="entity:railiance",
            order_class="new-order",
            estimate_eur=Decimal("999.00"),
            signal=restricted(),
        )
        self.assertEqual(result["decision"], "admit")
        self.assertEqual(result["reason"], "railiance-self-use")

    def test_cli_refuses_restricted_overage(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            path = Path(tmp) / "signal.json"
            path.write_text(json.dumps([restricted()]), encoding="utf-8")
            code = cm.main(
                [
                    "check",
                    "--entity",
                    "entity:coulomb",
                    "--estimate-eur",
                    "50",
                    "--signal",
                    str(path),
                ]
            )
            self.assertEqual(code, 2)

    def test_cli_admits_open(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            path = Path(tmp) / "signal.json"
            path.write_text("[]", encoding="utf-8")
            code = cm.main(
                [
                    "check",
                    "--entity",
                    "entity:coulomb",
                    "--estimate-eur",
                    "50",
                    "--signal",
                    str(path),
                ]
            )
            self.assertEqual(code, 0)
