"""Encryption and storage logic for brokr secrets."""

import fcntl
import json
import os
from pathlib import Path

from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt

def get_store_dir() -> Path:
    return Path(os.environ.get("BROKR_DIR", str(Path.home() / ".brokr")))


BROKR_DIR = Path.home() / ".brokr"


class BrokrError(Exception):
    """Base exception for brokr-specific errors."""


def derive_key(passphrase: str, salt: bytes) -> bytes:
    kdf = Scrypt(salt=salt, length=32, n=2**17, r=8, p=1)
    return kdf.derive(passphrase.encode("utf-8"))


def encrypt(data: bytes, key: bytes) -> bytes:
    nonce = os.urandom(12)
    aesgcm = AESGCM(key)
    ciphertext = aesgcm.encrypt(nonce, data, None)
    return nonce + ciphertext


def decrypt(data: bytes, key: bytes) -> bytes:
    nonce = data[:12]
    ciphertext = data[12:]
    aesgcm = AESGCM(key)
    try:
        return aesgcm.decrypt(nonce, ciphertext, None)
    except InvalidTag:
        raise BrokrError("Decryption failed: wrong passphrase or corrupted data.")


def _lock_path(store_dir: Path) -> Path:
    return store_dir / ".lock"


def load_store(passphrase: str, store_dir: Path = BROKR_DIR) -> dict:
    store_file = store_dir / "store.enc"
    salt_file = store_dir / "salt"

    if not store_file.exists():
        return {"secrets": {}}

    salt = salt_file.read_bytes()
    key = derive_key(passphrase, salt)
    encrypted = store_file.read_bytes()
    plaintext = decrypt(encrypted, key)
    return json.loads(plaintext)


def save_store(data: dict, passphrase: str, store_dir: Path = BROKR_DIR) -> None:
    store_dir.mkdir(parents=True, exist_ok=True)
    store_file = store_dir / "store.enc"
    salt_file = store_dir / "salt"
    lock_file = _lock_path(store_dir)

    # Acquire exclusive lock for write safety
    lock_fd = os.open(str(lock_file), os.O_CREAT | os.O_RDWR, 0o600)
    try:
        fcntl.flock(lock_fd, fcntl.LOCK_EX)

        if salt_file.exists():
            salt = salt_file.read_bytes()
        else:
            salt = os.urandom(16)
            salt_file.write_bytes(salt)
            os.chmod(salt_file, 0o600)

        key = derive_key(passphrase, salt)
        plaintext = json.dumps(data).encode("utf-8")
        encrypted = encrypt(plaintext, key)
        store_file.write_bytes(encrypted)
        os.chmod(store_file, 0o600)
    finally:
        fcntl.flock(lock_fd, fcntl.LOCK_UN)
        os.close(lock_fd)
