# 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_clover import utils as clover_utils

_logger = logging.getLogger(__name__)


class CloverTerminal(models.Model):
    _name = 'clover.terminal'
    _description = 'Clover Terminal Device'
    _order = 'name'

    name = fields.Char(
        string="Terminal Name",
        required=True,
        help="A friendly name for this terminal. You can rename it to "
             "identify the location (e.g. 'Front Desk', 'Back Office').",
    )
    clover_device_name = fields.Char(
        string="Clover Device Name",
        readonly=True,
        help="The original device name from Clover (read-only).",
    )
    serial_number = fields.Char(
        string="Serial Number",
        help="The Clover device serial number. Used as X-Clover-Device-Id header.",
        required=True,
        copy=False,
    )
    device_id = fields.Char(
        string="Device ID",
        help="The Clover device UUID from the Platform API.",
        copy=False,
    )
    provider_id = fields.Many2one(
        'payment.provider',
        string="Payment Provider",
        required=True,
        ondelete='cascade',
        domain="[('code', '=', 'clover')]",
    )
    model_name = fields.Char(
        string="Device Model",
        readonly=True,
    )
    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_serial_provider = models.Constraint(
        'UNIQUE(serial_number, provider_id)',
        'A terminal with this serial number already exists for this provider.',
    )

    # === BUSINESS METHODS === #

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

    def action_refresh_status(self):
        """Check terminal status via the Clover Platform API.

        First tries the Cloud Pay Display ping (POST /connect/v1/device/ping).
        If that fails (e.g. REST Pay Display not configured), falls back to
        the Platform API device endpoint (GET /v3/merchants/{mId}/devices/{deviceId}).
        """
        self.ensure_one()
        provider = self._get_provider_sudo()

        # --- Attempt 1: Cloud Pay Display ping ---
        try:
            provider._clover_terminal_request(
                'POST', 'device/ping',
                serial_number=self.serial_number,
            )
            self.write({
                'status': 'online',
                'last_seen': fields.Datetime.now(),
            })
            return provider._clover_notification(
                _("Terminal '%(name)s' is online.", name=self.name),
                'success',
            )
        except (ValidationError, UserError):
            _logger.debug(
                "Cloud ping failed for %s, trying Platform API.",
                self.serial_number,
            )

        # --- Attempt 2: Platform API device lookup ---
        if not self.device_id:
            self.status = 'unknown'
            return provider._clover_notification(
                _("Could not reach terminal '%(name)s'. "
                  "Cloud Pay Display may not be configured for this merchant.",
                  name=self.name),
                'warning',
            )

        try:
            result = provider._clover_make_platform_request(
                'GET', f'devices/{self.device_id}',
            )
            # Clover Platform API doesn't return real-time online/offline,
            # but a successful response means the device is registered.
            self.write({
                'status': 'online',
                'last_seen': fields.Datetime.now(),
            })
            return provider._clover_notification(
                _("Terminal '%(name)s' is registered and active on Clover.",
                  name=self.name),
                'success',
            )
        except (ValidationError, UserError) as e:
            self.status = 'offline'
            return provider._clover_notification(
                _("Could not reach terminal '%(name)s': %(error)s",
                  name=self.name, error=str(e)),
                'danger',
            )

    def action_send_payment(self, amount, currency, reference, capture=True):
        """Send a payment request to the Clover terminal via Cloud REST Pay API.

        :param float amount: The payment amount in major currency units.
        :param recordset currency: The currency record.
        :param str reference: The Odoo payment reference / externalPaymentId.
        :param bool capture: Whether to capture immediately (sale) or pre-auth.
        :return: The terminal payment response.
        :rtype: dict
        :raises UserError: If the terminal is offline.
        """
        self.ensure_one()

        if self.status == 'offline':
            raise UserError(
                _("Terminal '%(name)s' appears to be offline. "
                  "Please check the device and try again.",
                  name=self.name)
            )

        minor_amount = clover_utils.format_clover_amount(amount, currency)

        payload = {
            'amount': minor_amount,
            'externalPaymentId': reference,
            'capture': capture,
        }

        provider = self._get_provider_sudo()
        result = provider._clover_terminal_request(
            'POST', 'payments',
            serial_number=self.serial_number,
            payload=payload,
        )

        _logger.info(
            "Payment request sent to terminal %s for %s %s (ref: %s)",
            self.serial_number, amount, currency.name, reference,
        )

        return result

    def action_send_refund(self, payment_id, amount=None):
        """Send a refund request to the terminal.

        :param str payment_id: The Clover payment UUID to refund.
        :param int amount: Optional partial refund amount in cents.
        :return: The terminal refund response.
        :rtype: dict
        """
        self.ensure_one()

        payload = {}
        if amount:
            payload['amount'] = amount
        else:
            payload['fullRefund'] = True

        provider = self._get_provider_sudo()
        return provider._clover_terminal_request(
            'POST', f'payments/{payment_id}/refunds',
            serial_number=self.serial_number,
            payload=payload,
        )

    def action_check_payment_status(self, external_payment_id):
        """Check the status of a terminal payment by externalPaymentId.

        :param str external_payment_id: The externalPaymentId sent with the payment.
        :return: Dict with status and payment data.
        :rtype: dict
        """
        self.ensure_one()

        provider = self._get_provider_sudo()
        try:
            result = provider._clover_terminal_request(
                'GET', f'payments?externalPaymentId={external_payment_id}',
                serial_number=self.serial_number,
            )

            payment = result.get('payment', {})
            if not payment:
                return {'status': 'pending', 'message': 'Waiting for terminal response...'}

            clover_result = payment.get('result', '')
            card_txn = payment.get('cardTransaction', {})
            state = card_txn.get('state', '')

            if clover_result == 'SUCCESS':
                return {
                    'status': state or 'CLOSED',
                    'payment_id': payment.get('id', ''),
                    'card_transaction': card_txn,
                    'amount': payment.get('amount', 0),
                    'result': clover_result,
                }

            if clover_result in ('FAIL', 'DECLINED'):
                return {
                    'status': 'DECLINED',
                    'message': payment.get('failureMessage', 'Payment declined'),
                    'result': clover_result,
                }

            return {
                'status': 'pending',
                'message': f'Status: {clover_result or "processing"}',
                'result': clover_result,
            }

        except (ValidationError, UserError):
            return {'status': 'error', 'message': 'Failed to check payment status.'}

    def action_display_welcome(self):
        """Reset the terminal to the welcome screen."""
        self.ensure_one()
        provider = self._get_provider_sudo()
        try:
            provider._clover_terminal_request(
                'POST', 'device/welcome',
                serial_number=self.serial_number,
            )
            return provider._clover_notification(
                _("Welcome screen sent to '%(name)s'.", name=self.name),
                'success',
            )
        except (ValidationError, UserError) as e:
            _logger.warning("Failed to display welcome on terminal %s: %s",
                            self.serial_number, e)
            return provider._clover_notification(
                _("Could not send welcome screen to '%(name)s': %(error)s",
                  name=self.name, error=str(e)),
                'danger',
            )

    def _get_terminal_callback_url(self):
        """Build the callback URL for terminal payment completion."""
        base_url = self._get_provider_sudo().get_base_url()
        return f"{base_url}/payment/clover/terminal/callback"
