"""Solana wallet management — load or generate an Ed25519 keypair.

Solana keypair format: 64-byte array stored as JSON list of ints.
  bytes 0-31: private scalar (seed)
  bytes 32-63: public key (on-curve point)
Wallet address: base58-encoded 32-byte public key.
"""

import json
from pathlib import Path

_DEFAULT_WALLET_PATH = Path.home() / ".config" / "meshnet" / "wallet.json"

_B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"


def _b58encode(data: bytes) -> str:
    n = int.from_bytes(data, "big")
    chars: list[str] = []
    while n:
        n, rem = divmod(n, 58)
        chars.append(_B58_ALPHABET[rem])
    leading = 0
    for b in data:
        if b == 0:
            leading += 1
        else:
            break
    return "1" * leading + "".join(reversed(chars))


def load_or_create_wallet(
    path: Path = _DEFAULT_WALLET_PATH,
) -> tuple[bytes, bytes, str]:
    """Return (secret_32, public_32, address_base58).

    Loads from *path* if it exists; otherwise generates a new keypair and
    saves it.  The parent directory is created if needed.
    """
    if path.exists():
        mode = path.stat().st_mode & 0o777
        if mode & 0o077:
            path.chmod(0o600)
        raw = bytes(json.loads(path.read_text()))
        if len(raw) != 64:
            raise ValueError(f"Wallet at {path} has unexpected length {len(raw)}, expected 64")
        secret, public = raw[:32], raw[32:]
        return secret, public, _b58encode(public)

    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
    from cryptography.hazmat.primitives.serialization import (
        Encoding, NoEncryption, PrivateFormat, PublicFormat,
    )

    priv = Ed25519PrivateKey.generate()
    secret = priv.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
    public = priv.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
    keypair = list(secret) + list(public)

    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(keypair))
    path.chmod(0o600)

    return secret, public, _b58encode(public)
