"""Peer identity — stable peer_id and RSA keypair, persisted to disk."""

from __future__ import annotations

import hashlib
import json
import os
import stat
from pathlib import Path

_DEFAULT_IDENTITY_PATH = Path.home() / ".config" / "meshnet" / "identity.json"


def _generate_keypair() -> tuple[bytes, bytes]:
    """Return (private_key_pem, public_key_pem) for a new RSA-2048 keypair."""
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives import serialization

    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    priv_pem = key.private_bytes(
        serialization.Encoding.PEM,
        serialization.PrivateFormat.PKCS8,
        serialization.NoEncryption(),
    )
    pub_pem = key.public_key().public_bytes(
        serialization.Encoding.PEM,
        serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    return priv_pem, pub_pem


def _peer_id_from_pubkey(pub_pem: bytes) -> str:
    return hashlib.sha256(pub_pem).hexdigest()[:16]


def load_or_create_identity(path: Path | None = None) -> dict:
    """Return identity dict with peer_id, private_key_pem, public_key_pem.

    Creates and persists a new identity if none exists at path.
    """
    p = path or _DEFAULT_IDENTITY_PATH
    if p.exists():
        try:
            data = json.loads(p.read_text())
            if "peer_id" in data and "public_key_pem" in data:
                return data
        except (json.JSONDecodeError, OSError):
            pass

    priv_pem, pub_pem = _generate_keypair()
    identity = {
        "peer_id": _peer_id_from_pubkey(pub_pem),
        "private_key_pem": priv_pem.decode(),
        "public_key_pem": pub_pem.decode(),
    }

    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(json.dumps(identity, indent=2))
    try:
        os.chmod(p, stat.S_IRUSR | stat.S_IWUSR)
    except OSError:
        pass

    return identity
