"""Data models for the Garanti BBVA Virtual POS (GVP) API.

This module defines all request and response data structures as Python
dataclasses. These models map directly to the XML elements used by the
GVP API protocol.

All fields default to ``None`` so that only the relevant fields need to
be populated for each transaction type.

Example::

    from garantipay.models import Card, Terminal, Request

    card = Card(number="1234567890123456", expire_date="0326", cvv2="123")
    terminal = Terminal(id="12345678", merchant_id="1234567")
"""

from dataclasses import dataclass
from typing import Optional


@dataclass
class Address:
    """Billing or shipping address associated with an order.

    Attributes:
        type: Address type. ``"B"`` for billing, ``"S"`` for shipping.
        name: Customer's first name.
        last_name: Customer's last name.
        company: Company name, if applicable.
        text: Full address text (street, building, etc.).
        city: City name.
        district: District or neighborhood.
        country: Country name or ISO country code.
        postal_code: ZIP or postal code.
        phone_number: Landline phone number.
        gsm_number: Mobile phone number.
        fax_number: Fax number.
    """

    type: Optional[str] = None
    name: Optional[str] = None
    last_name: Optional[str] = None
    company: Optional[str] = None
    text: Optional[str] = None
    city: Optional[str] = None
    district: Optional[str] = None
    country: Optional[str] = None
    postal_code: Optional[str] = None
    phone_number: Optional[str] = None
    gsm_number: Optional[str] = None
    fax_number: Optional[str] = None


@dataclass
class AddressList:
    """Container for address information within an order.

    Attributes:
        address: The address associated with this order.
    """

    address: Optional[Address] = None


@dataclass
class Order:
    """Order information for a payment transaction.

    Attributes:
        order_id: Unique identifier for the order. If left empty, the
            gateway will auto-generate one. Required for refund operations.
        group_id: Group identifier for linking related transactions.
        address_list: Billing/shipping address details.
    """

    order_id: Optional[str] = None
    group_id: Optional[str] = None
    address_list: Optional[AddressList] = None


@dataclass
class Card:
    """Credit/debit card information.

    .. warning::
        Handle card data with extreme care. Never log or persist raw card
        numbers. Ensure PCI-DSS compliance in your application.

    Attributes:
        number: Full card number (PAN). Example: ``"1234567890123456"``.
        expire_date: Expiration date in ``MMYY`` format. Example: ``"0326"``
            for December 2026.
        cvv2: The 3-digit security code on the back of the card
            (or 4-digit for AMEX).
    """

    number: Optional[str] = None
    expire_date: Optional[str] = None
    cvv2: Optional[str] = None


@dataclass
class Customer:
    """Customer information attached to a transaction.

    Attributes:
        ip_address: The customer's IP address. **Required** by the gateway
            for all transactions.
        email_address: Customer's email address. Optional but recommended.
    """

    ip_address: Optional[str] = None
    email_address: Optional[str] = None


@dataclass
class Terminal:
    """Terminal (POS device) identification and authentication.

    These values are provided by Garanti BBVA when you register
    for a virtual POS account.

    Attributes:
        merchant_id: Merchant ID (Isyeri No) assigned by the bank.
        prov_user_id: Provisioning user ID for the transaction type.
            Typically ``"PROVAUT"`` for sales or ``"PROVRFN"`` for refunds.
        user_id: API user ID for authentication.
        id: Terminal ID (Terminal No) assigned by the bank.
        hash_data: Computed SHA-1 hash for request authentication.
            Use :func:`garantipay.hash.compute_hash_data` to generate this.
    """

    merchant_id: Optional[str] = None
    prov_user_id: Optional[str] = None
    user_id: Optional[str] = None
    id: Optional[str] = None
    hash_data: Optional[str] = None


@dataclass
class Secure3D:
    """3D Secure authentication data.

    Populate this when processing transactions that have been authenticated
    via 3D Secure (Verified by Visa, Mastercard SecureCode, etc.).

    Attributes:
        authentication_code: Authentication verification value from the
            3D Secure process.
        security_level: Security level indicator. Common values:
            ``"3D"`` for full 3D Secure, ``"3D_PAY"`` for 3D Pay model.
        txn_id: Transaction ID from the 3D Secure authentication.
        md: Merchant data returned from the 3D Secure authentication callback.
    """

    authentication_code: Optional[str] = None
    security_level: Optional[str] = None
    txn_id: Optional[str] = None
    md: Optional[str] = None


@dataclass
class Transaction:
    """Transaction details for the payment request.

    Attributes:
        type: Transaction type. Common values:

            - ``"sales"`` -- Standard sale/purchase.
            - ``"refund"`` -- Refund a previous transaction.
            - ``"void"`` -- Cancel a transaction before settlement.
            - ``"preauth"`` -- Pre-authorization (hold funds).
            - ``"postauth"`` -- Capture a pre-authorized amount.

        sub_type: Transaction sub-type, if applicable.
        firm_card_no: Firm/corporate card number, if applicable.
        installment_cnt: Number of installments. Leave empty or ``""``
            for single payment. Example: ``"3"`` for 3 installments.
        amount: Transaction amount in minor currency units (last 2 digits
            are fractional). Example: ``"100"`` means 1.00 TRY.
        currency_code: ISO 4217 numeric currency code. Use the
            :data:`garantipay.CURRENCIES` mapping for convenience.
        cardholder_present_code: Cardholder presence indicator.
            ``"13"`` when processing 3D Secure transactions.
        moto_ind: Mail Order / Telephone Order indicator.
            ``"N"`` for standard e-commerce, ``"Y"`` for MOTO.
        description: Optional transaction description/note.
        secure_3d: 3D Secure authentication data, if applicable.
    """

    type: Optional[str] = None
    sub_type: Optional[str] = None
    firm_card_no: Optional[str] = None
    installment_cnt: Optional[str] = None
    amount: Optional[str] = None
    currency_code: Optional[str] = None
    cardholder_present_code: Optional[str] = None
    moto_ind: Optional[str] = None
    description: Optional[str] = None
    secure_3d: Optional[Secure3D] = None


@dataclass
class Request:
    """Complete GVP API request payload.

    This is the top-level model that aggregates all components of a
    payment request. Build this object and pass it to
    :meth:`garantipay.GarantiClient.execute` or
    :meth:`garantipay.API.transaction`.

    Attributes:
        mode: Environment mode. ``"TEST"`` for sandbox, ``"PROD"`` for production.
        version: API version string. Typically ``"v1.0"``.
        channel_code: Channel code, if required by your integration.
        terminal: Terminal/POS identification and authentication data.
        customer: Customer information (IP address is required).
        card: Credit/debit card details.
        order: Order and address information.
        transaction: Transaction type, amount, and parameters.
    """

    mode: Optional[str] = None
    version: Optional[str] = None
    channel_code: Optional[str] = None
    terminal: Optional[Terminal] = None
    customer: Optional[Customer] = None
    card: Optional[Card] = None
    order: Optional[Order] = None
    transaction: Optional[Transaction] = None


# ---------------------------------------------------------------------------
# Response models
# ---------------------------------------------------------------------------


@dataclass
class ResponseTransaction:
    """Parsed response status from the gateway.

    Attributes:
        source: Source of the response (e.g. ``"HOST"``, ``"GVPS"``).
        code: Response code. ``"00"`` indicates success; any other value
            indicates an error.
        reason_code: Additional reason code for the response.
        message: Human-readable result message (e.g. ``"Approved"``).
        error_msg: Error message, populated when the transaction fails.
        sys_err_msg: System-level error message from the bank's backend.
    """

    source: Optional[str] = None
    code: Optional[str] = None
    reason_code: Optional[str] = None
    message: Optional[str] = None
    error_msg: Optional[str] = None
    sys_err_msg: Optional[str] = None


@dataclass
class RewardInqResult:
    """Reward/loyalty program inquiry results.

    Attributes:
        reward_list: Reward point information, if available.
        cheque_list: Cheque/voucher information, if available.
    """

    reward_list: Optional[str] = None
    cheque_list: Optional[str] = None


@dataclass
class TransactionResponse:
    """Detailed transaction response from the gateway.

    Attributes:
        response: Status information including success/error codes.
        retref_num: Retrieval reference number assigned by the bank.
            Keep this for future reference (e.g. for refunds or voids).
        auth_code: Authorization code from the issuing bank.
        batch_num: Settlement batch number.
        sequence_num: Sequence number within the batch.
        prov_date: Provisioning/processing date.
        card_number_masked: Masked version of the card number
            (e.g. ``"12345678****1234"``).
        card_holder_name: Cardholder name as registered with the bank.
        card_type: Card brand/type identifier.
        hash_data: Response hash for verification.
        host_msg_list: Additional messages from the host system.
        reward_inq_result: Reward program inquiry results, if requested.
        garanti_card_ind: Indicates whether the card is a Garanti card.
    """

    response: Optional[ResponseTransaction] = None
    retref_num: Optional[str] = None
    auth_code: Optional[str] = None
    batch_num: Optional[str] = None
    sequence_num: Optional[str] = None
    prov_date: Optional[str] = None
    card_number_masked: Optional[str] = None
    card_holder_name: Optional[str] = None
    card_type: Optional[str] = None
    hash_data: Optional[str] = None
    host_msg_list: Optional[str] = None
    reward_inq_result: Optional[RewardInqResult] = None
    garanti_card_ind: Optional[str] = None


@dataclass
class Response:
    """Complete GVP API response.

    This is the top-level response object returned by
    :meth:`garantipay.API.transaction` and
    :meth:`garantipay.GarantiClient.execute`.

    To check if the transaction was successful::

        if response.is_successful:
            print("Transaction approved!")
        else:
            print(f"Error: {response.error_message}")

    Attributes:
        mode: The mode in which the transaction was processed.
        terminal: Terminal information echoed back.
        customer: Customer information echoed back.
        card: Card information echoed back (usually masked).
        order: Order information echoed back.
        transaction: Detailed transaction result.
    """

    mode: Optional[str] = None
    terminal: Optional[Terminal] = None
    customer: Optional[Customer] = None
    card: Optional[Card] = None
    order: Optional[Order] = None
    transaction: Optional[TransactionResponse] = None

    @property
    def is_successful(self) -> bool:
        """Check whether the transaction was approved.

        Returns:
            ``True`` if the response code is ``"00"`` (approved),
            ``False`` otherwise.
        """
        if self.transaction and self.transaction.response:
            return self.transaction.response.code == "00"
        return False

    @property
    def error_message(self) -> Optional[str]:
        """Retrieve the error message from a failed transaction.

        Returns:
            The error message string, or ``None`` if no error is present.
        """
        if self.transaction and self.transaction.response:
            return self.transaction.response.error_msg
        return None

    @property
    def response_code(self) -> Optional[str]:
        """Retrieve the response code.

        Returns:
            The response code string (e.g. ``"00"`` for success),
            or ``None`` if not available.
        """
        if self.transaction and self.transaction.response:
            return self.transaction.response.code
        return None

    @property
    def response_message(self) -> Optional[str]:
        """Retrieve the human-readable response message.

        Returns:
            The response message string, or ``None`` if not available.
        """
        if self.transaction and self.transaction.response:
            return self.transaction.response.message
        return None
