"""Persona schema parsing + active-hours window tests."""
from __future__ import annotations

import json
from datetime import datetime

from decnet.realism.personas import (
    EmailPersona,
    in_active_hours,
    login_for,
    parse_personas,
)


def _persona(**over) -> dict:
    base = {
        "name": "John Smith",
        "email": "john@corp.com",
        "role": "COO",
        "tone": "formal",
        "mannerisms": ["uses 'Best regards'"],
    }
    base.update(over)
    return base


def test_parse_empty_inputs():
    assert parse_personas(None) == []
    assert parse_personas("") == []
    assert parse_personas([]) == []


def test_parse_invalid_json_returns_empty_no_raise():
    assert parse_personas("{not json") == []


def test_parse_invalid_top_level_shape_returns_empty():
    assert parse_personas('{"not": "a list"}') == []


def test_parse_drops_invalid_entry_keeps_valid():
    raw = json.dumps([
        _persona(),
        {"name": "broken", "email": "not-an-email"},
        _persona(name="Sarah", email="sarah@corp.com"),
    ])
    parsed = parse_personas(raw)
    assert len(parsed) == 2
    assert {p.name for p in parsed} == {"John Smith", "Sarah"}


def test_parse_resolves_language_default_when_unset():
    raw = json.dumps([_persona()])
    parsed = parse_personas(raw, language_default="es")
    assert parsed[0].language == "es"


def test_parse_persona_language_overrides_default():
    raw = json.dumps([_persona(language="pt")])
    parsed = parse_personas(raw, language_default="es")
    assert parsed[0].language == "pt"


def test_parse_accepts_python_list_directly():
    parsed = parse_personas([_persona()])
    assert len(parsed) == 1


def test_uses_llms_heavily_default_false():
    parsed = parse_personas([_persona()])
    assert parsed[0].uses_llms_heavily is False


def test_uses_llms_heavily_can_be_set():
    parsed = parse_personas([_persona(uses_llms_heavily=True)])
    assert parsed[0].uses_llms_heavily is True


def _dt(h: int, m: int = 0) -> datetime:
    return datetime(2024, 1, 15, h, m)


def test_active_hours_normal_window():
    p = EmailPersona(**_persona(active_hours="09:00-18:00"))
    assert in_active_hours(p, _dt(12)) is True
    assert in_active_hours(p, _dt(8)) is False
    assert in_active_hours(p, _dt(18)) is False
    assert in_active_hours(p, _dt(9)) is True


def test_active_hours_wraparound_window():
    p = EmailPersona(**_persona(active_hours="22:00-06:00"))
    assert in_active_hours(p, _dt(23)) is True
    assert in_active_hours(p, _dt(0)) is True
    assert in_active_hours(p, _dt(5)) is True
    assert in_active_hours(p, _dt(7)) is False


def test_active_hours_malformed_treats_as_always_on():
    p = EmailPersona(**_persona(active_hours="garbage"))
    assert in_active_hours(p, _dt(0)) is True
    assert in_active_hours(p, _dt(23)) is True


def test_active_hours_equal_window_treated_as_always_on():
    p = EmailPersona(**_persona(active_hours="10:00-10:00"))
    assert in_active_hours(p, _dt(5)) is True


def test_active_hours_minute_precision_start_boundary():
    p = EmailPersona(**_persona(active_hours="09:30-17:45"))
    assert in_active_hours(p, _dt(9, 15)) is False
    assert in_active_hours(p, _dt(9, 29)) is False
    assert in_active_hours(p, _dt(9, 30)) is True
    assert in_active_hours(p, _dt(17, 44)) is True
    assert in_active_hours(p, _dt(17, 45)) is False


def test_active_hours_minute_precision_wraparound():
    p = EmailPersona(**_persona(active_hours="22:30-06:15"))
    assert in_active_hours(p, _dt(22, 29)) is False
    assert in_active_hours(p, _dt(22, 30)) is True
    assert in_active_hours(p, _dt(6, 14)) is True
    assert in_active_hours(p, _dt(6, 15)) is False


def test_login_for_normalises_display_name():
    assert login_for("John Smith") == "johnsmith"
    assert login_for("alice") == "alice"


def test_login_for_falls_back_to_user_on_punctuation():
    # The realism namer and canary cultivator both rely on this so the
    # decky filesystem doesn't end up with an unexpected username.
    assert login_for("Mr. Robot") == "user"
    assert login_for("") == "user"
    assert login_for("Renée") == "user"  # non-ASCII falls back


def test_login_for_shared_by_naming_and_cultivator():
    """Single source of truth: realism naming and canary cultivator
    must agree on the persona→login mapping."""
    from decnet.canary import cultivator
    from decnet.realism import naming
    persona = "John Smith"
    expected = login_for(persona)
    assert naming._home(persona) == f"/home/{expected}"
    # cultivator imports login_for; not duplicated.
    assert cultivator.login_for is login_for
