# Part of Odoo. See LICENSE file for full copyright and licensing details.

import json
import logging

from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError

from odoo.addons.fusion_poynt import utils as poynt_utils

_logger = logging.getLogger(__name__)


class PoyntTerminal(models.Model):
    _name = 'poynt.terminal'
    _description = 'Poynt Terminal Device'
    _order = 'name'

    name = fields.Char(
        string="Terminal Name",
        required=True,
    )
    device_id = fields.Char(
        string="Device ID",
        help="The Poynt device identifier (urn:tid:...).",
        required=True,
        copy=False,
    )
    serial_number = fields.Char(
        string="Serial Number",
        copy=False,
    )
    provider_id = fields.Many2one(
        'payment.provider',
        string="Payment Provider",
        required=True,
        ondelete='cascade',
        domain="[('code', '=', 'poynt')]",
    )
    store_id_poynt = fields.Char(
        string="Poynt Store ID",
        help="The Poynt store UUID this terminal belongs to.",
    )
    status = fields.Selection(
        selection=[
            ('online', "Online"),
            ('offline', "Offline"),
            ('unknown', "Unknown"),
        ],
        string="Status",
        default='unknown',
        readonly=True,
    )
    last_seen = fields.Datetime(
        string="Last Seen",
        readonly=True,
    )
    active = fields.Boolean(
        default=True,
    )

    _unique_device_provider = models.Constraint(
        'UNIQUE(device_id, provider_id)',
        'A terminal with this device ID already exists for this provider.',
    )

    # === BUSINESS METHODS === #

    def _get_provider_sudo(self):
        return self.provider_id.sudo()

    def action_refresh_status(self):
        """Refresh the terminal status from Poynt Cloud."""
        for terminal in self:
            try:
                provider = terminal._get_provider_sudo()
                store_id = terminal.store_id_poynt or provider.poynt_store_id
                if store_id:
                    endpoint = f'stores/{store_id}/storeDevices/{terminal.device_id}'
                else:
                    endpoint = f'storeDevices/{terminal.device_id}'

                result = provider._poynt_make_request('GET', endpoint)
                poynt_status = result.get('status', 'UNKNOWN')

                if poynt_status == 'ACTIVATED':
                    terminal.status = 'online'
                elif poynt_status in ('DEACTIVATED', 'INACTIVE'):
                    terminal.status = 'offline'
                else:
                    terminal.status = 'unknown'

                terminal.last_seen = fields.Datetime.now()
            except (ValidationError, UserError) as e:
                _logger.warning(
                    "Failed to refresh status for terminal %s: %s",
                    terminal.device_id, e,
                )
                terminal.status = 'unknown'

    def action_send_payment_to_terminal(self, amount, currency, reference, order_id=None):
        """Push a payment request to the physical Poynt terminal.

        This sends a cloud message to the terminal device instructing it
        to start a payment collection for the given amount.

        :param float amount: The payment amount in major currency units.
        :param recordset currency: The currency record.
        :param str reference: The Odoo payment reference.
        :param str order_id: Optional Poynt order UUID to link.
        :return: The Poynt cloud message response.
        :rtype: dict
        :raises UserError: If the terminal is offline.
        """
        self.ensure_one()

        if self.status == 'offline':
            raise UserError(
                _("Terminal '%(name)s' is offline. Please check the device.",
                  name=self.name)
            )

        minor_amount = poynt_utils.format_poynt_amount(amount, currency)

        payment_request = {
            'amount': minor_amount,
            'currency': currency.name,
            'referenceId': reference,
            'callbackUrl': self._get_terminal_callback_url(),
            'skipReceiptScreen': False,
            'debit': True,
        }

        if order_id:
            payment_request['orderId'] = order_id

        provider = self._get_provider_sudo()
        store_id = self.store_id_poynt or provider.poynt_store_id or ''

        data_str = json.dumps({
            'action': 'sale',
            'purchaseAmount': minor_amount,
            'tipAmount': 0,
            'currency': currency.name,
            'referenceId': reference,
            'callbackUrl': self._get_terminal_callback_url(),
        })

        try:
            result = provider._poynt_make_request(
                'POST',
                'cloudMessages',
                business_scoped=False,
                payload={
                    'businessId': provider.poynt_business_id,
                    'storeId': store_id,
                    'deviceId': self.device_id,
                    'ttl': 300,
                    'serialNum': self.serial_number or '',
                    'data': data_str,
                },
            )
            _logger.info(
                "Payment request sent to terminal %s for %s %s (ref: %s)",
                self.device_id, amount, currency.name, reference,
            )
            return result
        except (ValidationError, UserError) as e:
            _logger.error(
                "Failed to send payment to terminal %s: %s",
                self.device_id, e,
            )
            raise

    def _get_terminal_callback_url(self):
        """Build the callback URL for terminal payment completion.

        :return: The full callback URL.
        :rtype: str
        """
        base_url = self._get_provider_sudo().get_base_url()
        return f"{base_url}/payment/poynt/terminal/callback"

    def action_check_terminal_payment_status(self, reference):
        """Poll for the status of a terminal payment.

        Searches Poynt transactions by referenceId (set via cloud message)
        and falls back to notes field.

        :param str reference: The Odoo transaction reference.
        :return: Dict with status and transaction data if completed.
        :rtype: dict
        """
        self.ensure_one()

        provider = self._get_provider_sudo()
        try:
            txn_result = provider._poynt_make_request(
                'GET',
                'transactions',
                params={
                    'referenceId': reference,
                    'limit': 5,
                },
            )

            transactions = txn_result.get('transactions', [])

            if not transactions:
                txn_result = provider._poynt_make_request(
                    'GET',
                    'transactions',
                    params={
                        'notes': reference,
                        'limit': 5,
                    },
                )
                transactions = txn_result.get('transactions', [])

            if not transactions:
                return {'status': 'pending', 'message': 'Waiting for terminal response...'}

            for txn in transactions:
                status = txn.get('status', 'UNKNOWN')
                if status in ('CAPTURED', 'AUTHORIZED', 'SETTLED'):
                    return {
                        'status': status,
                        'transaction_id': txn.get('id', ''),
                        'funding_source': txn.get('fundingSource', {}),
                        'amounts': txn.get('amounts', {}),
                    }

            txn = transactions[0]
            return {
                'status': txn.get('status', 'UNKNOWN'),
                'transaction_id': txn.get('id', ''),
                'funding_source': txn.get('fundingSource', {}),
                'amounts': txn.get('amounts', {}),
            }
        except (ValidationError, UserError):
            return {'status': 'error', 'message': 'Failed to check payment status.'}
