"""Spending conditions, sighash computation, and ECDSA signing.

Implements the SIP-005 authorization / signing algorithm:
1. Build cleared spending condition (signature zeroed)
2. Serialize unsigned tx → SHA-512/256 → initial sighash
3. SHA-512/256(sighash + auth_type + fee + nonce) → presign sighash
4. coincurve sign_recoverable → reorder RSV→VRS
5. Replace zero signature with real one
"""

from __future__ import annotations

import hashlib

from coincurve import PrivateKey

from .constants import (
    AUTH_TYPE_STANDARD,
    SINGLE_SIG_SPENDING_CONDITION,
    SIGNATURE_LENGTH,
)
from .keys import private_key_to_public_key, hash160


def _sha512_256(data: bytes) -> bytes:
    """Compute SHA-512/256 hash."""
    return hashlib.new("sha512_256", data).digest()


def create_spending_condition(
    public_key_hash: bytes,
    nonce: int,
    fee: int,
    *,
    cleared: bool = True,
) -> dict:
    """Create a spending condition dict.

    When cleared=True, the signature is set to all zeros (for sighash computation).
    """
    return {
        "hash_mode": SINGLE_SIG_SPENDING_CONDITION,
        "signer": public_key_hash,  # 20 bytes
        "nonce": nonce,
        "fee": fee,
        "key_encoding": 0x00,  # compressed
        "signature": b"\x00" * SIGNATURE_LENGTH if cleared else b"",
    }


def serialize_spending_condition(cond: dict) -> bytes:
    """Serialize a spending condition to bytes.

    Format: hash_mode(1) + signer(20) + nonce(8 BE) + fee(8 BE)
            + key_encoding(1) + signature(65)
    """
    result = bytes([cond["hash_mode"]])
    result += cond["signer"]  # 20 bytes
    result += cond["nonce"].to_bytes(8, "big")
    result += cond["fee"].to_bytes(8, "big")
    result += bytes([cond["key_encoding"]])
    result += cond["signature"]
    return result


def serialize_authorization(auth_type: int, spending_condition: dict) -> bytes:
    """Serialize a full authorization (type + spending condition)."""
    return bytes([auth_type]) + serialize_spending_condition(spending_condition)


def _serialize_unsigned_tx(
    version: int,
    chain_id: int,
    auth_type: int,
    spending_condition: dict,
    anchor_mode: int,
    post_condition_mode: int,
    post_conditions: bytes,
    payload: bytes,
) -> bytes:
    """Serialize a full transaction (with cleared auth for signing)."""
    result = bytes([version])
    result += chain_id.to_bytes(4, "big")
    result += serialize_authorization(auth_type, spending_condition)
    result += bytes([anchor_mode])
    result += bytes([post_condition_mode])
    result += post_conditions
    result += payload
    return result


def compute_presign_sighash(
    version: int,
    chain_id: int,
    auth_type: int,
    spending_condition: dict,
    anchor_mode: int,
    post_condition_mode: int,
    post_conditions: bytes,
    payload: bytes,
    *,
    actual_fee: int | None = None,
    actual_nonce: int | None = None,
) -> bytes:
    """Compute the presign sighash for transaction signing.

    Steps:
    1. Serialize unsigned tx with cleared auth (fee/nonce zeroed) → SHA-512/256 → initial sighash
    2. SHA-512/256(sighash + auth_type + actual_fee + actual_nonce) → presign sighash
    """
    # Step 1: initial sighash from cleared tx
    unsigned_tx = _serialize_unsigned_tx(
        version, chain_id, auth_type, spending_condition,
        anchor_mode, post_condition_mode, post_conditions, payload,
    )
    initial_sighash = _sha512_256(unsigned_tx)

    # Step 2: presign sighash uses actual fee/nonce
    fee = actual_fee if actual_fee is not None else spending_condition["fee"]
    nonce = actual_nonce if actual_nonce is not None else spending_condition["nonce"]
    presign_input = initial_sighash
    presign_input += bytes([auth_type])
    presign_input += fee.to_bytes(8, "big")
    presign_input += nonce.to_bytes(8, "big")
    return _sha512_256(presign_input)


def _sign_recoverable(private_key_hex: str, message_hash: bytes) -> bytes:
    """Sign a message hash with recoverable ECDSA, returning VRS (65 bytes).

    coincurve returns RSV (r[32] + s[32] + v[1]).
    Stacks expects VRS (v[1] + r[32] + s[32]).
    """
    key_hex = private_key_hex.removeprefix("0x")
    if len(key_hex) == 66 and key_hex.endswith("01"):
        key_hex = key_hex[:64]
    pk = PrivateKey(bytes.fromhex(key_hex))
    sig = pk.sign_recoverable(message_hash, hasher=None)
    # sig is 65 bytes: r(32) + s(32) + recovery_id(1)
    r = sig[:32]
    s = sig[32:64]
    v = sig[64]
    return bytes([v]) + r + s


def sign_transaction(
    private_key_hex: str,
    version: int,
    chain_id: int,
    auth_type: int,
    nonce: int,
    fee: int,
    anchor_mode: int,
    post_condition_mode: int,
    post_conditions: bytes,
    payload: bytes,
) -> bytes:
    """Sign a transaction and return the fully serialized signed tx bytes.

    1. Derive pubkey hash from private key
    2. Create cleared spending condition
    3. Compute presign sighash
    4. Sign with ECDSA recoverable
    5. Build final tx with real signature
    """
    # Derive public key hash
    pubkey = private_key_to_public_key(private_key_hex)
    pubkey_hash = hash160(pubkey)

    # Create cleared spending condition (nonce/fee zeroed for initial sighash,
    # actual values used in presign sighash step)
    cleared_cond = create_spending_condition(pubkey_hash, nonce=0, fee=0, cleared=True)

    # Compute presign sighash (uses actual fee/nonce, not cleared ones)
    sighash = compute_presign_sighash(
        version, chain_id, auth_type, cleared_cond,
        anchor_mode, post_condition_mode, post_conditions, payload,
        actual_fee=fee, actual_nonce=nonce,
    )

    # Sign
    signature = _sign_recoverable(private_key_hex, sighash)

    # Build final spending condition with real signature and actual fee/nonce
    signed_cond = {
        **cleared_cond,
        "nonce": nonce,
        "fee": fee,
        "key_encoding": 0x00,  # compressed
        "signature": signature,
    }

    # Serialize the complete transaction
    result = bytes([version])
    result += chain_id.to_bytes(4, "big")
    result += serialize_authorization(auth_type, signed_cond)
    result += bytes([anchor_mode])
    result += bytes([post_condition_mode])
    result += post_conditions
    result += payload
    return result
