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

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 CloverPaymentWizard(models.TransientModel):
    _name = 'clover.payment.wizard'
    _description = 'Collect Clover Payment'

    invoice_id = fields.Many2one(
        'account.move',
        string="Invoice",
        required=True,
        readonly=True,
        domain="[('move_type', 'in', ('out_invoice', 'out_refund'))]",
    )
    partner_id = fields.Many2one(
        related='invoice_id.partner_id',
        string="Customer",
    )
    amount = fields.Monetary(
        string="Amount",
        required=True,
        currency_field='currency_id',
    )
    currency_id = fields.Many2one(
        'res.currency',
        string="Currency",
        required=True,
        readonly=True,
    )
    provider_id = fields.Many2one(
        'payment.provider',
        string="Clover Provider",
        required=True,
        domain="[('code', '=', 'clover'), ('state', '!=', 'disabled')]",
    )
    provider_name = fields.Char(
        related='provider_id.name',
        string="Clover Provider",
        readonly=True,
    )

    # --- Payment mode (terminal vs manual card) ---
    payment_mode = fields.Selection(
        selection=[
            ('terminal', "Terminal"),
            ('card', "Manual Card Entry"),
        ],
        string="Payment Mode",
        default='terminal',
        required=True,
    )

    # --- Terminal fields ---
    terminal_id = fields.Many2one(
        'clover.terminal',
        string="Terminal",
        domain="[('provider_id', '=', provider_id), ('active', '=', True)]",
    )

    # --- Card type & surcharge fields ---
    card_type = fields.Selection(
        selection=[
            ('visa', "Visa"),
            ('mastercard', "Mastercard"),
            ('amex', "American Express"),
            ('debit', "Debit"),
            ('other', "Other"),
        ],
        string="Card Type",
    )
    surcharge_enabled = fields.Boolean(
        compute='_compute_surcharge_enabled',
    )
    surcharge_rate = fields.Float(
        string="Surcharge Rate (%)",
        digits=(5, 2),
        readonly=True,
    )
    surcharge_amount = fields.Monetary(
        string="Surcharge Amount",
        currency_field='currency_id',
        readonly=True,
    )
    surcharge_applied = fields.Boolean(default=False)
    original_amount = fields.Monetary(
        string="Invoice Amount",
        currency_field='currency_id',
        readonly=True,
    )

    # --- Card entry fields (never stored, transient only) ---
    card_number = fields.Char(string="Card Number")
    exp_month = fields.Char(string="Exp. Month", size=2)
    exp_year = fields.Char(string="Exp. Year", size=4)
    cvv = fields.Char(string="CVV", size=4)
    cardholder_name = fields.Char(string="Cardholder Name")

    # --- Status tracking ---
    state = fields.Selection(
        selection=[
            ('draft', "Draft"),
            ('waiting', "Waiting for Terminal"),
            ('done', "Payment Collected"),
            ('error', "Error"),
        ],
        default='draft',
    )
    status_message = fields.Text(string="Status", readonly=True)
    clover_charge_id = fields.Char(readonly=True)
    clover_payment_id = fields.Char(
        string="Terminal Payment ID",
        readonly=True,
        help="The Clover payment UUID from the terminal response.",
    )
    sent_at = fields.Datetime(
        string="Sent to Terminal At",
        readonly=True,
    )
    transaction_id = fields.Many2one(
        'payment.transaction',
        string="Payment Transaction",
        readonly=True,
    )

    @api.depends_context('uid')
    def _compute_surcharge_enabled(self):
        enabled = self.env['ir.config_parameter'].sudo().get_param(
            'fusion_clover.surcharge_enabled', 'False',
        ) == 'True'
        for rec in self:
            rec.surcharge_enabled = enabled

    @staticmethod
    def _detect_card_brand(card_number):
        num = (card_number or '').replace(' ', '')
        if len(num) < 2:
            return 'other'
        if num[:2] in ('34', '37'):
            return 'amex'
        if num[0] == '4':
            return 'visa'
        prefix2 = int(num[:2])
        if 51 <= prefix2 <= 55:
            return 'mastercard'
        if len(num) >= 4:
            prefix4 = int(num[:4])
            if 2221 <= prefix4 <= 2720:
                return 'mastercard'
        return 'other'

    def _get_surcharge_rate(self, card_type):
        ICP = self.env['ir.config_parameter'].sudo()
        rate_key = {
            'visa': 'fusion_clover.surcharge_visa_rate',
            'mastercard': 'fusion_clover.surcharge_mastercard_rate',
            'amex': 'fusion_clover.surcharge_amex_rate',
            'debit': 'fusion_clover.surcharge_debit_rate',
        }.get(card_type, 'fusion_clover.surcharge_other_rate')
        return float(ICP.get_param(rate_key, '0') or 0)

    @api.onchange('card_number')
    def _onchange_card_number(self):
        if self.card_number:
            self.card_type = self._detect_card_brand(self.card_number)

    @api.onchange('card_type')
    def _onchange_card_type(self):
        if not self.card_type or not self.surcharge_enabled:
            self.surcharge_rate = 0.0
            self.surcharge_amount = 0.0
            return
        rate = self._get_surcharge_rate(self.card_type)
        base_amount = self.original_amount or self.amount
        self.surcharge_rate = rate
        self.surcharge_amount = round(base_amount * rate / 100.0, 2)

    @api.model
    def default_get(self, fields_list):
        res = super().default_get(fields_list)
        invoice_id = self.env.context.get('active_id')
        active_model = self.env.context.get('active_model')

        if active_model == 'account.move' and invoice_id:
            invoice = self.env['account.move'].browse(invoice_id)
            res['invoice_id'] = invoice.id
            res['amount'] = invoice.amount_residual
            res['original_amount'] = invoice.amount_residual
            res['currency_id'] = invoice.currency_id.id

        provider = self.env['payment.provider'].sudo().search([
            ('code', '=', 'clover'),
            ('state', '!=', 'disabled'),
        ], limit=1)
        if provider:
            res['provider_id'] = provider.id
            if provider.clover_default_terminal_id:
                res['terminal_id'] = provider.clover_default_terminal_id.id

        return res

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

    def _apply_surcharge_if_needed(self):
        """Add the surcharge invoice line if surcharge is enabled and not yet applied."""
        if self.surcharge_applied or not self.surcharge_enabled:
            return
        if not self.card_type:
            raise UserError(_("Please select the card type to calculate the surcharge."))

        rate = self._get_surcharge_rate(self.card_type)
        if rate <= 0:
            return

        base_amount = self.original_amount or self.amount
        fee_amount = round(base_amount * rate / 100.0, 2)
        if fee_amount <= 0:
            return

        ICP = self.env['ir.config_parameter'].sudo()
        product_id = int(ICP.get_param('fusion_clover.surcharge_product_id', '0') or 0)
        product = self.env['product.product'].sudo().browse(product_id).exists()
        if not product:
            product = self.env.ref('fusion_clover.product_cc_processing_fee', raise_if_not_found=False)
        if not product:
            raise UserError(
                _("Surcharge product not configured. "
                  "Go to Settings > Fusion Clover to set it up.")
            )

        invoice = self.invoice_id.sudo()
        was_posted = invoice.state == 'posted'
        if was_posted:
            invoice.button_draft()

        description = _("Credit Card Processing Fee (%(rate).2f%% surcharge)", rate=rate)
        invoice.write({
            'invoice_line_ids': [(0, 0, {
                'product_id': product.id,
                'name': description,
                'quantity': 1,
                'price_unit': fee_amount,
                'tax_ids': [(5, 0, 0)],
            })],
        })

        if was_posted:
            invoice.action_post()

        self.write({
            'surcharge_applied': True,
            'surcharge_rate': rate,
            'surcharge_amount': fee_amount,
            'amount': invoice.amount_residual,
        })

    def _remove_surcharge_line(self):
        """Remove the surcharge line from the invoice if it was applied."""
        if not self.surcharge_applied:
            return

        ICP = self.env['ir.config_parameter'].sudo()
        product_id = int(ICP.get_param('fusion_clover.surcharge_product_id', '0') or 0)
        product = self.env['product.product'].sudo().browse(product_id).exists()
        if not product:
            product = self.env.ref('fusion_clover.product_cc_processing_fee', raise_if_not_found=False)
        if not product:
            return

        invoice = self.invoice_id.sudo()
        surcharge_lines = invoice.invoice_line_ids.filtered(
            lambda l: l.product_id.id == product.id
        )
        if not surcharge_lines:
            self.surcharge_applied = False
            return

        was_posted = invoice.state == 'posted'
        if was_posted:
            invoice.button_draft()

        surcharge_lines.unlink()

        if was_posted:
            invoice.action_post()

        self.write({
            'surcharge_applied': False,
            'surcharge_amount': 0.0,
            'surcharge_rate': 0.0,
            'amount': invoice.amount_residual,
        })

    def action_collect_payment(self):
        """Process a payment - either via terminal or manual card entry."""
        self.ensure_one()
        if self.payment_mode == 'terminal':
            return self._collect_via_terminal()
        return self._collect_via_card()

    def _collect_via_terminal(self):
        """Send payment to Clover terminal via Cloud REST Pay Display API."""
        self.ensure_one()
        self._apply_surcharge_if_needed()

        if self.amount <= 0:
            raise UserError(_("Payment amount must be greater than zero."))
        if not self.terminal_id:
            raise UserError(_("Please select a terminal device."))

        self._cleanup_draft_transaction()
        tx = self._create_payment_transaction()
        reference = tx.reference

        try:
            provider = self._get_provider_sudo()
            capture = not provider.capture_manually

            result = self.terminal_id.action_send_payment(
                amount=self.amount,
                currency=self.currency_id,
                reference=reference,
                capture=capture,
            )

            # The terminal response may contain the payment immediately
            # (if the customer already tapped/swiped), or it may be pending.
            payment = result.get('payment', {})
            payment_id = payment.get('id', '')

            if payment and payment.get('result') == 'SUCCESS':
                # Payment completed immediately
                card_txn = payment.get('cardTransaction', {})
                tx.write({
                    'clover_charge_id': payment_id,
                    'provider_reference': payment_id,
                })
                payment_data = {
                    'reference': reference,
                    'clover_charge_id': payment_id,
                    'clover_status': 'succeeded',
                    'source': {
                        'brand': card_txn.get('cardType', ''),
                        'last4': card_txn.get('last4', ''),
                    },
                }
                tx._process('clover', payment_data)

                self.write({
                    'state': 'done',
                    'status_message': _(
                        "Payment collected successfully. Payment ID: %(pid)s",
                        pid=payment_id,
                    ),
                    'clover_payment_id': payment_id,
                })
                return self._reopen_wizard()

            # Payment sent to terminal, waiting for customer interaction
            self.write({
                'state': 'waiting',
                'status_message': _("Payment sent to terminal. Waiting for customer..."),
                'sent_at': fields.Datetime.now(),
                'clover_payment_id': payment_id or '',
            })
            return self._reopen_wizard()

        except (ValidationError, UserError) as e:
            self._cleanup_draft_transaction()
            self._remove_surcharge_line()
            self.write({
                'state': 'error',
                'status_message': str(e),
            })
            return self._reopen_wizard()

    def action_check_status(self):
        """Poll the terminal for payment completion status."""
        self.ensure_one()
        if not self.terminal_id or not self.transaction_id:
            raise UserError(_("No terminal or transaction to check."))

        tx = self.transaction_id
        reference = tx.reference

        result = self.terminal_id.action_check_payment_status(reference)
        status = result.get('status', 'pending')

        if status in ('CLOSED', 'AUTH', 'AUTHORIZED', 'CAPTURED'):
            payment_id = result.get('payment_id', '')
            card_txn = result.get('card_transaction', {})

            tx.write({
                'clover_charge_id': payment_id or tx.clover_charge_id,
                'provider_reference': payment_id or tx.provider_reference,
            })
            payment_data = {
                'reference': reference,
                'clover_charge_id': payment_id,
                'clover_status': 'succeeded',
                'source': {
                    'brand': card_txn.get('cardType', ''),
                    'last4': card_txn.get('last4', ''),
                },
            }
            tx._process('clover', payment_data)

            self.write({
                'state': 'done',
                'status_message': _(
                    "Payment collected successfully. Payment ID: %(pid)s",
                    pid=payment_id,
                ),
                'clover_payment_id': payment_id,
            })

        elif status in ('DECLINED', 'FAIL', 'FAILED'):
            tx._set_error(
                _("Payment was declined by the terminal.")
            )
            self._cleanup_draft_transaction()
            self._remove_surcharge_line()
            self.write({
                'state': 'error',
                'status_message': result.get('message', _("Payment declined at terminal.")),
            })

        elif status == 'error':
            self.write({
                'status_message': result.get('message', _("Error checking status.")),
            })

        else:
            self.write({
                'status_message': result.get('message', _("Still waiting for terminal...")),
            })

        return self._reopen_wizard()

    def _collect_via_card(self):
        """Process a manual card entry payment via Clover Ecommerce API."""
        self.ensure_one()
        self._apply_surcharge_if_needed()

        if self.amount <= 0:
            raise UserError(_("Payment amount must be greater than zero."))

        self._validate_card_fields()
        self._cleanup_draft_transaction()

        tx = self._create_payment_transaction()
        reference = tx.reference

        try:
            provider = self._get_provider_sudo()
            capture = not provider.capture_manually

            minor_amount = clover_utils.format_clover_amount(
                self.amount, self.currency_id,
            )

            payload = {
                'amount': minor_amount,
                'currency': self.currency_id.name.lower(),
                'capture': capture,
                'ecomind': 'moto',
                'description': reference,
                'source': self.card_number.replace(' ', ''),
                'metadata': {
                    'odoo_reference': reference,
                },
            }

            result = provider._clover_make_ecom_request(
                'POST', 'v1/charges', payload=payload,
            )

            charge_id = result.get('id', '')
            status = result.get('status', '')

            tx.write({
                'clover_charge_id': charge_id,
                'provider_reference': charge_id,
            })

            payment_data = {
                'reference': reference,
                'clover_charge_id': charge_id,
                'clover_status': status,
                'source': result.get('source', {}),
            }

            if status == 'failed':
                tx._set_error(
                    _("Payment was %(status)s by the processor.", status=status)
                )
                self._cleanup_draft_transaction()
                self._remove_surcharge_line()
                outcome = result.get('outcome', {})
                decline_msg = outcome.get('type', status)
                self.write({
                    'state': 'error',
                    'status_message': _(
                        "Payment %(status)s: %(reason)s",
                        status=status,
                        reason=decline_msg,
                    ),
                    'clover_charge_id': charge_id,
                })
                return self._reopen_wizard()

            tx._process('clover', payment_data)

            self.write({
                'state': 'done',
                'status_message': _(
                    "Payment collected successfully. Charge: %(charge_id)s",
                    charge_id=charge_id,
                ),
                'clover_charge_id': charge_id,
            })

            return self._reopen_wizard()

        except (ValidationError, UserError) as e:
            self._cleanup_draft_transaction()
            self._remove_surcharge_line()
            self.write({
                'state': 'error',
                'status_message': str(e),
            })
            return self._reopen_wizard()

    def action_send_receipt(self):
        """Email the payment receipt to the customer and close the wizard."""
        self.ensure_one()
        tx = self.transaction_id
        if not tx:
            raise UserError(_("No payment transaction found."))

        template = self.env.ref(
            'fusion_clover.mail_template_clover_receipt', raise_if_not_found=False,
        )
        if not template:
            raise UserError(_("Receipt email template not found."))

        template.send_mail(tx.id, force_send=True)

        return {'type': 'ir.actions.act_window_close'}

    def action_cancel_payment(self):
        """Cancel the payment and clean up the draft transaction."""
        self.ensure_one()
        self._cleanup_draft_transaction()
        self._remove_surcharge_line()
        return {'type': 'ir.actions.act_window_close'}

    def _cleanup_draft_transaction(self):
        """Remove the draft payment transaction created by this wizard."""
        if not self.transaction_id:
            return
        tx = self.transaction_id.sudo()
        if tx.state == 'draft':
            tx.invoice_ids = [(5,)]
            tx.unlink()
            self.transaction_id = False

    # === HELPERS === #

    def _validate_card_fields(self):
        """Validate that card entry fields are properly filled."""
        if not self.card_number or len(self.card_number.replace(' ', '')) < 13:
            raise UserError(_("Please enter a valid card number."))
        if not self.exp_month or not self.exp_month.isdigit():
            raise UserError(_("Please enter a valid expiry month (01-12)."))
        if not self.exp_year or not self.exp_year.isdigit() or len(self.exp_year) < 2:
            raise UserError(_("Please enter a valid expiry year."))
        if not self.cvv or not self.cvv.isdigit():
            raise UserError(_("Please enter the CVV."))

    def _create_payment_transaction(self):
        """Create a payment.transaction linked to the invoice."""
        PaymentMethod = self.env['payment.method'].sudo().with_context(active_test=False)
        payment_method = PaymentMethod.search(
            [('code', '=', 'card')], limit=1,
        )
        if not payment_method:
            payment_method = PaymentMethod.search(
                [('code', 'in', ('visa', 'mastercard'))], limit=1,
            )
        if not payment_method:
            raise UserError(
                _("No card payment method found. Please configure one "
                  "in Settings > Payment Methods.")
            )

        tx_values = {
            'provider_id': self.provider_id.id,
            'payment_method_id': payment_method.id,
            'amount': self.amount,
            'currency_id': self.currency_id.id,
            'partner_id': self.partner_id.id,
            'operation': 'offline',
            'invoice_ids': [(4, self.invoice_id.id)],
        }
        tx = self.env['payment.transaction'].sudo().create(tx_values)
        self.transaction_id = tx
        return tx

    def _reopen_wizard(self):
        """Return an action that re-opens this wizard record (keeps state)."""
        return {
            'type': 'ir.actions.act_window',
            'name': _("Collect Clover Payment"),
            'res_model': self._name,
            'res_id': self.id,
            'views': [(False, 'form')],
            'target': 'new',
        }
