"""
Comprehensive tests for ``bal.core.plugin_base``.

Covers BalTimestamp, BalConfig, and BalPlugin static helpers.

Run:
    source electrum/env/bin/activate
    python3 tests/test_core_plugin_base.py
"""

import os
import sys
import time

sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))

from datetime import date, datetime, timedelta

from bal.core.plugin_base import BalConfig, BalPlugin, BalTimestamp

# ------------------------------------------------------------------ #
# BalTimestamp
# ------------------------------------------------------------------ #

def test_bt_create_and_str():
    bt = BalTimestamp("30d")
    assert bt.unit == "d"
    assert bt.value == 30

    bt2 = BalTimestamp("1y")
    assert bt2.unit == "y"
    assert bt2.value == 1

    bt3 = BalTimestamp(1700000000)
    assert bt3.unit is None
    assert bt3.value == 1700000000

    bt4 = BalTimestamp("garbage")
    # fallback: value=1, unit=None
    assert bt4.value == 1
    assert bt4.unit is None

    bt5 = BalTimestamp(0)
    assert bt5.unit is None
    assert bt5.value == 0

    bt6 = BalTimestamp("7d")
    assert str(bt6) == "7d"

    bt7 = BalTimestamp("2y")
    assert str(bt7) == "2y"

    # absolute timestamp str -> ISO format
    bt8 = BalTimestamp(1700000000)
    s = str(bt8)
    assert "202" in s or "197" in s  # year present


def test_bt_duration_to_days():
    assert BalTimestamp("30d").duration_to_days() == 30
    assert BalTimestamp("1y").duration_to_days() == 365
    assert BalTimestamp("0d").duration_to_days() == 0
    assert BalTimestamp(1700000000).duration_to_days() == 1700000000  # unit None -> raw value


def test_bt_to_date_absolute():
    bt = BalTimestamp(1700000000)
    d = bt.to_date()
    assert isinstance(d, datetime)

    # absolute with from_date (should be ignored for absolute)
    d2 = bt.to_date(from_date=datetime(2020, 1, 1))
    assert d == d2


def test_bt_to_date_relative():
    now = datetime.now()

    # relative days from now
    bt = BalTimestamp("7d")
    d = bt.to_date()
    assert d.hour == 0 and d.minute == 0  # normalized to midnight
    assert d > now

    # reverse (subtract days)
    d_rev = bt.to_date(reverse=True)
    assert d_rev < now

    # from explicit datetime
    base = datetime(2025, 6, 1, 12, 0, 0)
    d = bt.to_date(from_date=base)
    expected = (base + timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0)
    assert d == expected

    # from int timestamp
    ts = int(base.timestamp())
    d = bt.to_date(from_date=ts)
    assert d == expected


def test_bt_to_date_years():
    bt = BalTimestamp("1y")
    d = bt.to_date()
    assert d > datetime.now()


def test_bt_to_date_overflow():
    """Huge relative durations should not crash (clamp to INT32_MAX)."""
    bt = BalTimestamp("999999999d")
    d = bt.to_date()
    # should not raise
    assert d is not None
    assert isinstance(d, datetime)


def test_bt_to_timestamp():
    bt = BalTimestamp("7d")
    ts = bt.to_timestamp()
    assert ts > time.time()
    assert isinstance(ts, float)

    bt2 = BalTimestamp(1700000000)
    assert abs(bt2.to_timestamp() - 1700000000) < 86400  # close to original


def test_bt_repr():
    assert repr(BalTimestamp("7d")) == "7d"
    r = repr(BalTimestamp(1700000000))
    assert isinstance(r, str)
    assert len(r) > 0


def test_bt_edge_values():
    # zero timestamp
    bt0 = BalTimestamp(0)
    d = bt0.to_date()
    assert d is not None

    # negative? (may depend on platform)
    try:
        bt_neg = BalTimestamp(-1)
        _ = bt_neg.to_date()
    except (OSError, ValueError, OverflowError):
        pass  # acceptable on some platforms


# ------------------------------------------------------------------ #
# BalTimestamp._safe_fromtimestamp
# ------------------------------------------------------------------ #

def test_safe_fromtimestamp_normal():
    d = BalTimestamp._safe_fromtimestamp(1700000000)
    assert isinstance(d, datetime)


def test_safe_fromtimestamp_nlocktime_max():
    """NLOCKTIME_MAX (2**32-1) must not raise even on 32-bit platforms."""
    d = BalTimestamp._safe_fromtimestamp(2**32 - 1)
    assert d is not None


def test_safe_fromtimestamp_negative():
    """Negative timestamps should not crash."""
    d = BalTimestamp._safe_fromtimestamp(-1)
    assert isinstance(d, datetime)


# ------------------------------------------------------------------ #
# BalConfig
# ------------------------------------------------------------------ #

class FakeConfig:
    """Minimal mock for Electrum config."""
    def __init__(self):
        self._store = {}
    def get(self, key, default=None):
        return self._store.get(key, default)
    def set_key(self, key, value, save=True):
        self._store[key] = value


def test_balconfig_default():
    cfg = FakeConfig()
    bc = BalConfig(cfg, "test_key", "default_val")
    assert bc.get() == "default_val"
    assert bc.get("override") == "override"
    assert bc.get(None) == "default_val"


def test_balconfig_set():
    cfg = FakeConfig()
    bc = BalConfig(cfg, "test_key", "default_val")
    bc.set("stored_val")
    assert cfg.get("test_key") == "stored_val"
    assert bc.get() == "stored_val"


# ------------------------------------------------------------------ #
# BalPlugin
# ------------------------------------------------------------------ #

def test_default_will_settings_relative():
    rel = BalPlugin.default_will_settings_relative()
    assert rel["threshold"] == "30d"
    assert rel["locktime"] == "1y"


def test_default_will_settings():
    settings = BalPlugin.default_will_settings()
    assert settings["baltx_fees"] == 20
    assert "threshold" in settings
    assert "locktime" in settings
    # threshold/locktime should be absolute timestamps
    assert isinstance(settings["threshold"], float)
    assert isinstance(settings["locktime"], float)


def test_default_will_settings_absolute():
    abs_ = BalPlugin.default_will_settings_absolute()
    assert "threshold" in abs_
    assert "locktime" in abs_
    # should be timestamps (in the future)
    today = datetime.combine(date.today(), datetime.min.time())
    assert abs_["threshold"] >= today.timestamp()
    assert abs_["locktime"] >= today.timestamp()


def test_validate_will_settings():
    # Note: passing None triggers `will_settings = []` which then fails
    # on .get(). This is a latent bug — test passing a dict directly
    result = BalPlugin.validate_will_settings(None, {"baltx_fees": 0})
    assert result["baltx_fees"] == 20

    # normal settings unchanged
    input_settings = {"baltx_fees": 50, "threshold": 1700000000, "locktime": 1800000000}
    result = BalPlugin.validate_will_settings(None, input_settings)
    assert result["baltx_fees"] == 50
    assert result["threshold"] == 1700000000


if __name__ == "__main__":
    test_bt_create_and_str()
    test_bt_duration_to_days()
    test_bt_to_date_absolute()
    test_bt_to_date_relative()
    test_bt_to_date_years()
    test_bt_to_date_overflow()
    test_bt_to_timestamp()
    test_bt_repr()
    test_bt_edge_values()
    test_safe_fromtimestamp_normal()
    test_safe_fromtimestamp_nlocktime_max()
    test_safe_fromtimestamp_negative()
    test_balconfig_default()
    test_balconfig_set()
    test_default_will_settings_relative()
    test_default_will_settings()
    test_default_will_settings_absolute()
    test_validate_will_settings()
    print(f"[OK] All {sum(1 for k in dir() if k.startswith('test_'))} plugin_base tests passed")
