"""Session + conversation state management."""
from __future__ import annotations

import os
import threading
import time
import uuid
from dataclasses import dataclass, field

from persistence import MUGI_DIR


SESSIONS_DIR = os.path.join(MUGI_DIR, "mesh", "sessions")


@dataclass
class MeshConversation:
    conversation_id: str
    conversation_type: str = "social"  # social | task
    message_count: int = 0
    max_messages: int = 24
    idle_ttl_seconds: int = 300
    wrap_up_at: int | None = 20
    state: str = "active"
    last_activity_ts: float = field(default_factory=time.time)

    def register_message(self) -> None:
        self.message_count += 1
        self.last_activity_ts = time.time()

    def should_wrap_up(self) -> bool:
        return self.wrap_up_at is not None and self.message_count >= self.wrap_up_at

    def should_close(self) -> bool:
        return self.message_count >= self.max_messages

    def idle_expired(self) -> bool:
        return (time.time() - self.last_activity_ts) > self.idle_ttl_seconds


@dataclass
class MeshSession:
    session_id: str
    peer_fingerprint: str
    peer_hostname: str
    peer_address: str
    peer_port: int
    session_key: bytes
    send_counter: int = 1
    recv_counter: int = 0
    _counter_lock: threading.Lock = field(default_factory=threading.Lock, repr=False)
    created_at: float = field(default_factory=time.time)
    conversations: dict[str, MeshConversation] = field(default_factory=dict)

    def get_or_create_conversation(self, conversation_id: str, conversation_type: str = "social") -> MeshConversation:
        convo = self.conversations.get(conversation_id)
        if convo is not None:
            return convo
        if conversation_type == "task":
            convo = MeshConversation(
                conversation_id=conversation_id,
                conversation_type="task",
                max_messages=50,
                idle_ttl_seconds=600,
                wrap_up_at=None,
            )
        else:
            convo = MeshConversation(
                conversation_id=conversation_id,
                conversation_type="social",
                max_messages=24,
                idle_ttl_seconds=300,
                wrap_up_at=20,
            )
        self.conversations[conversation_id] = convo
        return convo

    def new_conversation(self, conversation_type: str = "social") -> MeshConversation:
        cid = uuid.uuid4().hex
        return self.get_or_create_conversation(cid, conversation_type=conversation_type)

    def next_counter(self) -> int:
        with self._counter_lock:
            c = self.send_counter
            self.send_counter += 1
            return c

    def accept_inbound_counter(self, counter: int) -> bool:
        """Reject replayed or duplicate AES-GCM nonces (counter must strictly increase)."""
        if counter < 0:
            return False
        with self._counter_lock:
            if counter <= self.recv_counter:
                return False
            self.recv_counter = counter
            return True


def persist_session_key(session_id: str, key: bytes) -> str:
    os.makedirs(SESSIONS_DIR, exist_ok=True)
    path = os.path.join(SESSIONS_DIR, f"{session_id}.session")
    fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
    try:
        os.write(fd, key)
    finally:
        os.close(fd)
    try:
        os.chmod(path, 0o600)
    except OSError:
        pass
    return path


def delete_session_key(session_id: str) -> None:
    path = os.path.join(SESSIONS_DIR, f"{session_id}.session")
    try:
        if os.path.isfile(path):
            os.remove(path)
    except OSError:
        pass
