"""Hashing utilities for Garanti BBVA Virtual POS authentication.

The GVP API requires SHA-1 based hash computation for request authentication.
This module provides both low-level and high-level functions for hash generation.

The hash computation follows this algorithm:

1. Hash the password concatenated with the zero-padded terminal ID::

       hash_password = SHA1(password + terminal_id.zfill(9)).upper()

2. Hash the relevant transaction fields concatenated with the password hash::

       hash_data = SHA1(order_id + terminal_id + card_number + amount + hash_password).upper()

Example::

    from garantipay.hash import compute_hash_data

    hash_data = compute_hash_data(
        password="my_secret",
        terminal_id="12345678",
        order_id="",
        card_number="1234567890123456",
        amount="100",
    )
"""

import hashlib
from typing import Optional


def sha1(data: str) -> str:
    """Compute the SHA-1 hex digest of a string.

    Args:
        data: The input string to hash. Will be encoded as UTF-8.

    Returns:
        The lowercase hexadecimal SHA-1 digest string.

    Example::

        >>> from garantipay.hash import sha1
        >>> sha1("hello")
        'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'
    """
    hasher = hashlib.sha1()
    hasher.update(data.encode("utf-8"))
    return hasher.hexdigest()


def compute_password_hash(password: str, terminal_id: str) -> str:
    """Compute the hashed password required by the GVP protocol.

    The GVP API requires the provisioning user password to be hashed
    together with the zero-padded (9 digits) terminal ID before use
    in the request hash computation.

    Args:
        password: The provisioning user's password (e.g. ``PROVAUT`` password).
        terminal_id: The terminal ID assigned by the bank.

    Returns:
        Uppercase hexadecimal SHA-1 hash of ``password + terminal_id.zfill(9)``.

    Example::

        >>> from garantipay.hash import compute_password_hash
        >>> compute_password_hash("123qweASD*", "12345678")  # doctest: +SKIP
        'A1B2C3...'
    """
    padded_terminal_id = f"{int(terminal_id):09d}"
    return sha1(password + padded_terminal_id).upper()


def compute_hash_data(
    password: str,
    terminal_id: str,
    order_id: Optional[str] = None,
    card_number: Optional[str] = None,
    amount: Optional[str] = None,
) -> str:
    """Compute the full hash data string for a GVP API request.

    This is the primary function for generating the ``HashData`` field
    required in the Terminal section of every GVP request.

    The hash is computed as::

        SHA1(order_id + terminal_id + card_number + amount + password_hash).upper()

    Where ``password_hash`` is the result of :func:`compute_password_hash`.

    Args:
        password: The provisioning user's password.
        terminal_id: The terminal ID assigned by the bank.
        order_id: The order ID. Use ``None`` or ``""`` if auto-generated.
        card_number: Full card number (PAN). Can be ``None`` for refunds.
        amount: Transaction amount in minor units (e.g. ``"100"`` for 1.00 TRY).

    Returns:
        Uppercase hexadecimal SHA-1 hash string ready to be set as
        ``request.terminal.hash_data``.

    Example::

        >>> from garantipay.hash import compute_hash_data
        >>> hash_data = compute_hash_data(
        ...     password="123qweASD*",
        ...     terminal_id="12345678",
        ...     order_id="",
        ...     card_number="1234567890123456",
        ...     amount="100",
        ... )
    """
    password_hash = compute_password_hash(password, terminal_id)
    raw = (
        f"{order_id or ''}"
        f"{terminal_id or ''}"
        f"{card_number or ''}"
        f"{amount or ''}"
        f"{password_hash}"
    )
    return sha1(raw).upper()
