# 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 const
from odoo.addons.fusion_clover import utils as clover_utils

_logger = logging.getLogger(__name__)


class CloverRefundWizard(models.TransientModel):
    _name = 'clover.refund.wizard'
    _description = 'Refund via Clover'

    credit_note_id = fields.Many2one(
        'account.move',
        string="Credit Note",
        required=True,
        readonly=True,
    )
    original_invoice_id = fields.Many2one(
        'account.move',
        string="Original Invoice",
        readonly=True,
    )
    partner_id = fields.Many2one(
        related='credit_note_id.partner_id',
        string="Customer",
    )
    amount = fields.Monetary(
        string="Refund 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,
        readonly=True,
    )
    provider_name = fields.Char(
        related='provider_id.name',
        string="Clover Provider",
        readonly=True,
    )
    original_transaction_id = fields.Many2one(
        'payment.transaction',
        string="Original Transaction",
        readonly=True,
    )
    original_clover_charge_id = fields.Char(
        string="Clover Charge ID",
        readonly=True,
    )
    card_info = fields.Char(
        string="Card Used",
        readonly=True,
    )

    # --- Transaction age & refund method ---
    transaction_age_days = fields.Integer(
        string="Transaction Age (days)",
        readonly=True,
    )
    refund_type = fields.Selection(
        selection=[
            ('referenced', "Referenced Refund"),
            ('non_referenced', "Non-Referenced Credit"),
        ],
        string="Refund Method",
        readonly=True,
    )
    refund_type_note = fields.Text(
        string="Note",
        readonly=True,
    )
    terminal_id = fields.Many2one(
        'clover.terminal',
        string="Terminal",
        domain="[('provider_id', '=', provider_id), ('active', '=', True)]",
        help="Optional: select a terminal if the customer's card is present. "
             "Leave empty to issue a non-referenced credit via the Ecommerce API.",
    )

    refund_transaction_id = fields.Many2one(
        'payment.transaction',
        string="Refund Transaction",
        readonly=True,
    )

    state = fields.Selection(
        selection=[
            ('confirm', "Confirm"),
            ('done', "Refunded"),
            ('error', "Error"),
        ],
        default='confirm',
    )
    status_message = fields.Text(string="Status", readonly=True)

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

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

        if active_model != 'account.move' or not credit_note_id:
            return res

        credit_note = self.env['account.move'].browse(credit_note_id)
        res['credit_note_id'] = credit_note.id
        res['amount'] = abs(credit_note.amount_residual) or abs(credit_note.amount_total)
        res['currency_id'] = credit_note.currency_id.id

        orig_tx = credit_note._get_original_clover_transaction()
        if not orig_tx:
            raise UserError(_(
                "No Clover payment transaction found for the original invoice. "
                "This credit note cannot be refunded via Clover."
            ))

        res['original_transaction_id'] = orig_tx.id
        res['provider_id'] = orig_tx.provider_id.id
        res['original_invoice_id'] = credit_note.reversed_entry_id.id
        res['original_clover_charge_id'] = orig_tx.clover_charge_id

        # Pre-select default terminal
        provider = orig_tx.provider_id.sudo()
        if provider.clover_default_terminal_id:
            res['terminal_id'] = provider.clover_default_terminal_id.id

        # Transaction age & refund method
        age_days = 0
        if orig_tx.create_date:
            age_days = (fields.Datetime.now() - orig_tx.create_date).days
        res['transaction_age_days'] = age_days

        if age_days > const.REFERENCED_REFUND_LIMIT_DAYS:
            res['refund_type'] = 'non_referenced'
            res['refund_type_note'] = _(
                "This transaction is %(days)s days old (limit is %(limit)s "
                "days). A non-referenced credit will be issued. This "
                "requires the customer's card to be present on the terminal.",
                days=age_days,
                limit=const.REFERENCED_REFUND_LIMIT_DAYS,
            )
        else:
            res['refund_type'] = 'referenced'
            res['refund_type_note'] = _(
                "This transaction is %(days)s days old (within the %(limit)s-day "
                "limit). A referenced refund will be issued back to the "
                "original card automatically.",
                days=age_days,
                limit=const.REFERENCED_REFUND_LIMIT_DAYS,
            )

        # Card info from receipt data
        receipt_data = orig_tx.clover_receipt_data
        if receipt_data:
            try:
                data = json.loads(receipt_data)
                card_brand = data.get('card_brand', '')
                card_last4 = data.get('card_last4', '')
                if card_brand or card_last4:
                    res['card_info'] = f"{card_brand} ****{card_last4}"
            except (ValueError, KeyError):
                pass

        return res

    def action_process_refund(self):
        """Dispatch to referenced refund or non-referenced credit."""
        self.ensure_one()

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

        orig_tx = self.original_transaction_id
        if orig_tx.clover_voided:
            raise UserError(_(
                "This transaction was already voided on %(date)s. "
                "A voided transaction cannot also be refunded — the charge "
                "was already reversed before settlement.",
                date=orig_tx.clover_void_date,
            ))

        # Pre-refund verification on Clover
        self._verify_not_already_reversed()

        if self.refund_type == 'non_referenced':
            return self._process_non_referenced_credit()
        return self._process_referenced_refund()

    def _verify_not_already_reversed(self):
        """Check on Clover that the charge hasn't been fully refunded already."""
        orig_tx = self.original_transaction_id
        charge_id = orig_tx.clover_charge_id or orig_tx.provider_reference
        if not charge_id:
            return

        provider = self._get_provider_sudo()
        try:
            provider._clover_verify_charge_not_reversed(charge_id)
        except (UserError, ValidationError):
            raise
        except Exception:
            _logger.debug("Could not verify charge %s before refund", charge_id)

    def _process_referenced_refund(self):
        """Send a referenced refund via Clover Ecommerce API."""
        orig_tx = self.original_transaction_id
        charge_id = orig_tx.clover_charge_id or orig_tx.provider_reference
        provider = self._get_provider_sudo()

        try:
            result = provider._clover_create_refund(
                charge_id=charge_id,
                amount=self.amount,
                currency=self.currency_id,
                reason=f'Refund for {orig_tx.reference} via {self.credit_note_id.name}',
            )
        except (ValidationError, UserError) as e:
            self.write({
                'state': 'error',
                'status_message': str(e),
            })
            return self._reopen_wizard()

        refund_status = result.get('status', '')
        refund_id = result.get('id', '')

        _logger.info(
            "Clover refund response: status=%s, id=%s",
            refund_status, refund_id,
        )

        if refund_status == 'failed':
            self.write({
                'state': 'error',
                'status_message': _(
                    "Refund declined by the payment processor. "
                    "Status: %(status)s. Please try again or contact support.",
                    status=refund_status,
                ),
            })
            return self._reopen_wizard()

        refund_tx = self._create_refund_transaction(orig_tx, refund_id, refund_status)
        self.refund_transaction_id = refund_tx

        self.credit_note_id.sudo().clover_refunded = True

        self.credit_note_id.sudo().message_post(
            body=_(
                "Refund processed via Clover. Amount: %(amount)s %(currency)s. "
                "Clover Refund ID: %(refund_id)s.",
                amount=self.amount,
                currency=self.currency_id.name,
                refund_id=refund_id,
            ),
        )

        self.write({
            'state': 'done',
            'status_message': _(
                "Refund of %(amount)s %(currency)s processed successfully. "
                "The refund will appear on the customer's card within "
                "3-5 business days.",
                amount=self.amount,
                currency=self.currency_id.name,
            ),
        })

        return self._reopen_wizard()

    def _process_non_referenced_credit(self):
        """Issue a non-referenced credit (manual refund).

        Two paths:
        1. If a terminal is selected → send to terminal via Cloud Pay Display
           (card-present, customer taps/inserts card on device).
        2. If no terminal → use Clover Ecommerce ``POST /v1/credits``
           (card-not-present, Clover issues credit to original card on file).

        Note: ``POST /v1/credits`` may not be enabled for all merchants.
        """
        provider = self._get_provider_sudo()
        orig_tx = self.original_transaction_id

        if self.terminal_id:
            return self._non_referenced_credit_via_terminal(provider, orig_tx)
        return self._non_referenced_credit_via_api(provider, orig_tx)

    def _non_referenced_credit_via_api(self, provider, orig_tx):
        """Issue a non-referenced credit via Clover Ecommerce API."""
        description = (
            f"Non-referenced credit for {orig_tx.reference} "
            f"via {self.credit_note_id.name}"
        )
        try:
            result = provider._clover_create_credit(
                amount=self.amount,
                currency=self.currency_id,
                description=description,
            )
        except (ValidationError, UserError) as e:
            self.write({
                'state': 'error',
                'status_message': _(
                    "%(error)s\n\nIf non-referenced credits are not enabled "
                    "for this merchant, select a terminal and ask the customer "
                    "to present their card on the device.",
                    error=str(e),
                ),
            })
            return self._reopen_wizard()

        credit_id = result.get('id', '')
        credit_status = result.get('status', 'succeeded')

        refund_tx = self._create_refund_transaction(orig_tx, credit_id, credit_status)
        self.refund_transaction_id = refund_tx
        self.credit_note_id.sudo().clover_refunded = True

        self.credit_note_id.sudo().message_post(
            body=_(
                "Non-referenced credit issued via Clover. "
                "Amount: %(amount)s %(currency)s. "
                "Clover Credit ID: %(credit_id)s.",
                amount=self.amount,
                currency=self.currency_id.name,
                credit_id=credit_id,
            ),
        )

        self.write({
            'state': 'done',
            'status_message': _(
                "Non-referenced credit of %(amount)s %(currency)s issued "
                "successfully. The credit will appear on the customer's "
                "card within 3-5 business days.",
                amount=self.amount,
                currency=self.currency_id.name,
            ),
        })
        return self._reopen_wizard()

    def _non_referenced_credit_via_terminal(self, provider, orig_tx):
        """Send a non-referenced credit to the terminal (card-present)."""
        minor_amount = clover_utils.format_clover_amount(
            self.amount, self.currency_id,
        )
        reference = f"NRC-{self.credit_note_id.name}"

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

        try:
            provider._clover_terminal_request(
                'POST', 'payments',
                serial_number=self.terminal_id.serial_number,
                payload=payload,
            )
        except (ValidationError, UserError) as e:
            self.write({
                'state': 'error',
                'status_message': str(e),
            })
            return self._reopen_wizard()

        refund_tx = self._create_refund_transaction(
            orig_tx, refund_id='', refund_status='PENDING',
        )
        self.refund_transaction_id = refund_tx
        self.credit_note_id.sudo().clover_refunded = True

        self.credit_note_id.sudo().message_post(
            body=_(
                "Non-referenced credit sent to terminal '%(terminal)s'. "
                "Amount: %(amount)s %(currency)s. "
                "The customer must present their card on the terminal to "
                "complete the refund.",
                terminal=self.terminal_id.name,
                amount=self.amount,
                currency=self.currency_id.name,
            ),
        )

        self.write({
            'state': 'done',
            'status_message': _(
                "Non-referenced credit of %(amount)s %(currency)s sent to "
                "terminal '%(terminal)s'. Please ask the customer to present "
                "their card on the terminal to complete the refund.",
                amount=self.amount,
                currency=self.currency_id.name,
                terminal=self.terminal_id.name,
            ),
        })
        return self._reopen_wizard()

    def _create_refund_transaction(self, orig_tx, refund_id, refund_status):
        """Create a payment.transaction for the refund."""
        PaymentMethod = self.env['payment.method'].sudo().with_context(active_test=False)
        payment_method = PaymentMethod.search(
            [('code', '=', 'card')], limit=1,
        ) or PaymentMethod.search(
            [('code', 'in', ('visa', 'mastercard'))], limit=1,
        )

        refund_tx = self.env['payment.transaction'].sudo().create({
            'provider_id': self.provider_id.id,
            'payment_method_id': payment_method.id if payment_method else False,
            'amount': -self.amount,
            'currency_id': self.currency_id.id,
            'partner_id': self.partner_id.id,
            'operation': 'refund',
            'source_transaction_id': orig_tx.id,
            'provider_reference': refund_id or '',
            'clover_charge_id': orig_tx.clover_charge_id,
            'clover_refund_id': refund_id or '',
            'invoice_ids': [(4, self.credit_note_id.id)],
        })

        if refund_id and refund_status not in ('PENDING',):
            payment_data = {
                'reference': refund_tx.reference,
                'clover_charge_id': orig_tx.clover_charge_id,
                'clover_refund_id': refund_id,
                'clover_status': refund_status or 'succeeded',
            }
            refund_tx._process('clover', payment_data)

        return refund_tx

    def action_send_receipt(self):
        """Email the refund receipt to the customer and close the wizard."""
        self.ensure_one()
        tx = self.refund_transaction_id
        if not tx:
            raise UserError(_("No refund 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 _reopen_wizard(self):
        return {
            'type': 'ir.actions.act_window',
            'name': _("Refund via Clover"),
            'res_model': self._name,
            'res_id': self.id,
            'views': [(False, 'form')],
            'target': 'new',
        }
