import logging
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

from odoo import _, fields, http
from odoo.http import request
from odoo.exceptions import UserError
from odoo.tools.misc import format_datetime as _odoo_format_datetime

COMPANY_TZ = 'America/Toronto'

_logger = logging.getLogger(__name__)


def _fmt_date(env, dt_val, tz=None):
    """Format a datetime for portal page display in the company timezone."""
    if not dt_val:
        return ''
    return _odoo_format_datetime(
        env, dt_val,
        tz=tz or COMPANY_TZ,
        dt_format="MMMM dd, yyyy 'at' hh:mm a",
    )


class FusionRentalController(http.Controller):

    # =================================================================
    # Cancellation (from renewal reminder email)
    # =================================================================

    @http.route(
        '/rental/cancel/<string:token>',
        type='http',
        auth='public',
        website=True,
        methods=['GET', 'POST'],
    )
    def rental_cancel(self, token, **kwargs):
        """Handle rental cancellation requests from email links."""
        cancel_request = request.env['rental.cancellation.request'].sudo().search([
            ('token', '=', token),
            ('state', '=', 'new'),
        ], limit=1)

        if not cancel_request:
            return request.render(
                'fusion_rental.cancellation_invalid_page',
                {'error': _("This cancellation link is invalid or has already been used.")},
            )

        order = cancel_request.order_id
        tz_name = order.company_id.partner_id.tz or COMPANY_TZ
        tz = ZoneInfo(tz_name)
        today = datetime.now(timezone.utc).astimezone(tz).date()
        if order.rental_next_renewal_date and order.rental_next_renewal_date <= today:
            return request.render(
                'fusion_rental.cancellation_invalid_page',
                {'error': _(
                    "Cancellation is not allowed on or after the renewal date. "
                    "Please contact our office for assistance."
                )},
            )

        if request.httprequest.method == 'POST':
            reason = kwargs.get('reason', '')
            cancel_request.write({'reason': reason})
            cancel_request.action_confirm()

            return request.render(
                'fusion_rental.cancellation_success_page',
                {
                    'order': order,
                    'partner': cancel_request.partner_id,
                },
            )

        return request.render(
            'fusion_rental.cancellation_form_page',
            {
                'order': order,
                'partner': cancel_request.partner_id,
                'token': token,
                'formatted_return_date': _fmt_date(
                    request.env, order.rental_return_date, tz_name,
                ),
            },
        )

    # =================================================================
    # Portal: Confirm Quotation → Redirect to Rental Agreement
    # =================================================================

    @http.route(
        '/rental/confirm-and-sign/<int:order_id>',
        type='http',
        auth='public',
        website=True,
        methods=['GET', 'POST'],
    )
    def rental_confirm_and_sign(self, order_id, access_token=None, **kwargs):
        """Confirm a rental quotation from the portal and redirect to the
        rental agreement signing page.

        This replaces Odoo's standard 'Sign & Pay' flow for rental orders.
        Uses the sale order's access_token for authentication so customers
        don't need a portal account.
        """
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or not order.is_rental_order
            or not access_token
            or order.access_token != access_token
        ):
            return request.redirect('/my/orders')

        if order.state in ('draft', 'sent'):
            order.action_confirm()

        if not order.rental_agreement_token:
            import uuid
            order.rental_agreement_token = uuid.uuid4().hex

        return request.redirect(
            f'/rental/agreement/{order.id}/{order.rental_agreement_token}'
        )

    # =================================================================
    # Rental Agreement Signing
    # =================================================================

    @http.route(
        '/rental/agreement/<int:order_id>/<string:token>',
        type='http',
        auth='public',
        website=True,
        methods=['GET'],
    )
    def rental_agreement_page(self, order_id, token, **kwargs):
        """Render the rental agreement signing page."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
            or order.rental_agreement_signed
        ):
            return request.render(
                'fusion_rental.cancellation_invalid_page',
                {'error': _("This agreement link is invalid or has already been signed.")},
            )

        google_api_key = request.env['ir.config_parameter'].sudo().get_param(
            'fusion_claims.google_maps_api_key', ''
        )
        if not google_api_key:
            google_api_key = request.env['ir.config_parameter'].sudo().get_param(
                'fusion_rental.google_maps_api_key', ''
            )

        poynt_business_id = ''
        poynt_application_id = ''
        provider = request.env['payment.provider'].sudo().search([
            ('code', '=', 'poynt'),
            ('state', '!=', 'disabled'),
        ], limit=1)
        if provider:
            poynt_business_id = provider.poynt_business_id or ''
            raw_app_id = provider.poynt_application_id or ''
            from odoo.addons.fusion_poynt.utils import clean_application_id
            poynt_application_id = clean_application_id(raw_app_id) or raw_app_id

        tz_name = order.company_id.partner_id.tz or COMPANY_TZ
        return request.render(
            'fusion_rental.agreement_signing_page',
            {
                'order': order,
                'partner': order.partner_id,
                'token': token,
                'pdf_preview_url': f'/rental/agreement/{order_id}/{token}/pdf',
                'google_api_key': google_api_key,
                'poynt_business_id': poynt_business_id,
                'poynt_application_id': poynt_application_id,
                'formatted_start_date': _fmt_date(
                    request.env, order.rental_start_date, tz_name,
                ),
                'formatted_return_date': _fmt_date(
                    request.env, order.rental_return_date, tz_name,
                ),
            },
        )

    @http.route(
        '/rental/agreement/<int:order_id>/<string:token>/pdf',
        type='http',
        auth='public',
        methods=['GET'],
    )
    def rental_agreement_pdf(self, order_id, token, **kwargs):
        """Serve the rental agreement PDF for preview (token-protected)."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if not order.exists() or order.rental_agreement_token != token:
            return request.not_found()

        report_name = 'fusion_rental.report_rental_agreement'
        report = request.env['ir.actions.report'].sudo()._get_report_from_name(report_name)
        if not report:
            report = request.env['ir.actions.report'].sudo()._get_report_from_name(
                'fusion_claims.report_rental_agreement'
            )
            report_name = 'fusion_claims.report_rental_agreement'
        if not report:
            return request.not_found()

        pdf_content, _report_type = report.sudo()._render_qweb_pdf(
            report_name, [order.id]
        )

        return request.make_response(
            pdf_content,
            headers=[
                ('Content-Type', 'application/pdf'),
                ('Content-Disposition',
                 f'inline; filename="{order.name} - {order.partner_id.name or ""}'
                 f' - Rental Agreement'
                 f'{" - Signed" if order.rental_agreement_signed else ""}.pdf"'),
            ],
        )

    @http.route(
        '/rental/agreement/<int:order_id>/<string:token>/sign',
        type='json',
        auth='public',
        methods=['POST'],
    )
    def rental_agreement_sign(self, order_id, token, **kwargs):
        """Process the agreement signing: save signature and tokenize card via nonce."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
            or order.rental_agreement_signed
        ):
            return {'success': False, 'error': 'Invalid or expired agreement link.'}

        signer_name = kwargs.get('signer_name', '').strip()
        signature_data = kwargs.get('signature_data', '')
        nonce = kwargs.get('nonce', '').strip()
        billing_address = kwargs.get('billing_address', '').strip()
        billing_city = kwargs.get('billing_city', '').strip()
        billing_state = kwargs.get('billing_state', '').strip()
        billing_postal_code = kwargs.get('billing_postal_code', '').strip()

        if not signer_name:
            return {'success': False, 'error': 'Full name is required.'}
        if not signature_data:
            return {'success': False, 'error': 'Signature is required.'}
        if not nonce:
            return {'success': False, 'error': 'Card authorization is required. Please fill in your card details.'}
        if not billing_postal_code:
            return {'success': False, 'error': 'Billing postal/zip code is required.'}

        sig_binary = signature_data
        if ',' in sig_binary:
            sig_binary = sig_binary.split(',')[1]

        full_billing = billing_address
        if billing_city:
            full_billing += f", {billing_city}"
        if billing_state:
            full_billing += f", {billing_state}"

        try:
            payment_token = self._tokenize_nonce_via_poynt(
                order, nonce, billing_postal_code,
            )
        except (UserError, Exception) as e:
            _logger.error("Nonce tokenization failed for %s: %s", order.name, e)
            return {'success': False, 'error': str(e)}

        order.write({
            'rental_agreement_signed': True,
            'rental_agreement_signature': sig_binary,
            'rental_agreement_signer_name': signer_name,
            'rental_agreement_signed_date': fields.Datetime.now(),
            'rental_payment_token_id': payment_token.id if payment_token else False,
            'rental_billing_address': full_billing,
            'rental_billing_postal_code': billing_postal_code,
        })

        order.message_post(
            body=_("Rental agreement signed by %s.", signer_name),
        )

        try:
            order._generate_and_attach_signed_agreement()
        except Exception as e:
            _logger.error(
                "Signed agreement PDF generation failed for %s: %s",
                order.name, e,
            )

        try:
            order._process_post_signing_payments()
        except Exception as e:
            _logger.error(
                "Post-signing payment processing failed for %s: %s",
                order.name, e,
            )

        return {
            'success': True,
            'message': 'Agreement signed successfully. Thank you!',
        }

    @http.route(
        '/rental/agreement/<int:order_id>/<string:token>/decline',
        type='json',
        auth='public',
        methods=['POST'],
    )
    def rental_agreement_decline(self, order_id, token, **kwargs):
        """Cancel/decline the rental order from the agreement page."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
            or order.rental_agreement_signed
        ):
            return {'success': False, 'error': 'Invalid or expired agreement link.'}

        try:
            order.action_cancel()
            order.message_post(
                body=_("Rental order declined by the customer from the agreement page."),
            )
        except Exception as e:
            _logger.error("Agreement decline failed for %s: %s", order.name, e)
            return {'success': False, 'error': 'Unable to cancel the order. Please contact our office.'}

        return {
            'success': True,
            'message': 'Your rental order has been cancelled. No charges have been applied.',
        }

    @http.route(
        '/rental/agreement/<int:order_id>/<string:token>/thank-you',
        type='http',
        auth='public',
        website=True,
        methods=['GET'],
    )
    def rental_agreement_thank_you(self, order_id, token, **kwargs):
        """Render the thank-you page after successful agreement signing."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
        ):
            return request.render(
                'fusion_rental.cancellation_invalid_page',
                {'error': _("This link is invalid.")},
            )

        company = order.company_id or request.env.company
        return request.render(
            'fusion_rental.agreement_thank_you_page',
            {
                'order': order,
                'partner': order.partner_id,
                'company_phone': company.phone or '',
                'company_email': company.email or '',
            },
        )

    def _tokenize_nonce_via_poynt(self, order, nonce, billing_postal_code=''):
        """Exchange a Poynt Collect nonce for a payment JWT and create a payment.token."""
        provider = request.env['payment.provider'].sudo().search([
            ('code', '=', 'poynt'),
            ('state', '!=', 'disabled'),
        ], limit=1)
        if not provider:
            raise UserError(_("Poynt payment provider is not configured."))

        result = provider._poynt_tokenize_nonce(nonce)

        card_data = result.get('card', {})
        card_id = card_data.get('id', '') or result.get('cardId', '')
        last_four = card_data.get('numberLast4', '')
        card_type = card_data.get('type', 'UNKNOWN')
        payment_jwt = result.get('paymentToken', '')

        avs_result = result.get('avsResult', {})
        if avs_result and billing_postal_code:
            avs_match = avs_result.get('postalCodeMatch', '')
            if avs_match and avs_match not in ('MATCH', 'YES', 'Y'):
                _logger.warning(
                    "AVS postal code mismatch for %s: expected=%s result=%s",
                    order.name, billing_postal_code, avs_match,
                )

        card_type_lower = card_type.lower() if card_type else ''
        PaymentMethod = request.env['payment.method'].sudo().with_context(active_test=False)
        payment_method = PaymentMethod.search(
            [('code', '=', card_type_lower)], limit=1,
        )
        if not payment_method:
            payment_method = PaymentMethod.search(
                [('code', '=', 'card')], limit=1,
            )
        if not payment_method:
            payment_method = PaymentMethod.search(
                [('code', 'in', ('visa', 'mastercard'))], limit=1,
            )

        token = request.env['payment.token'].sudo().create({
            'provider_id': provider.id,
            'payment_method_id': payment_method.id,
            'partner_id': order.partner_id.id,
            'provider_ref': card_id or nonce[:40],
            'poynt_card_id': card_id,
            'poynt_payment_token': payment_jwt,
            'payment_details': f"{card_type} ending in {last_four}",
        })
        return token

    # =================================================================
    # Card Reauthorization
    # =================================================================

    @http.route(
        '/rental/reauthorize/<int:order_id>/<string:token>',
        type='http',
        auth='public',
        website=True,
        methods=['GET'],
    )
    def rental_reauthorize_page(self, order_id, token, **kwargs):
        """Render the card reauthorization portal page."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
            or not order.is_rental_order
        ):
            return request.render(
                'fusion_rental.cancellation_invalid_page',
                {'error': _("This link is invalid or has expired.")},
            )

        google_api_key = request.env['ir.config_parameter'].sudo().get_param(
            'fusion_claims.google_maps_api_key', ''
        )
        if not google_api_key:
            google_api_key = request.env['ir.config_parameter'].sudo().get_param(
                'fusion_rental.google_maps_api_key', ''
            )

        poynt_business_id = ''
        poynt_application_id = ''
        provider = request.env['payment.provider'].sudo().search([
            ('code', '=', 'poynt'),
            ('state', '!=', 'disabled'),
        ], limit=1)
        if provider:
            poynt_business_id = provider.poynt_business_id or ''
            raw_app_id = provider.poynt_application_id or ''
            from odoo.addons.fusion_poynt.utils import clean_application_id
            poynt_application_id = clean_application_id(raw_app_id) or raw_app_id

        return request.render(
            'fusion_rental.card_reauthorization_page',
            {
                'order': order,
                'partner': order.partner_id,
                'google_api_key': google_api_key,
                'poynt_business_id': poynt_business_id,
                'poynt_application_id': poynt_application_id,
            },
        )

    @http.route(
        '/rental/reauthorize/<int:order_id>/<string:token>/submit',
        type='json',
        auth='public',
        methods=['POST'],
    )
    def rental_reauthorize_submit(self, order_id, token, **kwargs):
        """Process the card reauthorization submission."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
            or not order.is_rental_order
        ):
            return {'success': False, 'error': 'Invalid or expired link.'}

        cardholder_name = kwargs.get('cardholder_name', '').strip()
        nonce = kwargs.get('nonce', '').strip()
        billing_address = kwargs.get('billing_address', '').strip()
        billing_city = kwargs.get('billing_city', '').strip()
        billing_state = kwargs.get('billing_state', '').strip()
        billing_postal_code = kwargs.get('billing_postal_code', '').strip()

        if not cardholder_name:
            return {'success': False, 'error': 'Cardholder name is required.'}
        if not nonce:
            return {'success': False, 'error': 'Card authorization is required.'}
        if not billing_postal_code:
            return {'success': False, 'error': 'Billing postal/zip code is required.'}

        full_billing = billing_address
        if billing_city:
            full_billing += f", {billing_city}"
        if billing_state:
            full_billing += f", {billing_state}"

        try:
            payment_token = self._tokenize_nonce_via_poynt(
                order, nonce, billing_postal_code,
            )
        except Exception as e:
            _logger.error(
                "Card reauthorization tokenization failed for %s: %s",
                order.name, e,
            )
            return {'success': False, 'error': str(e)}

        old_token = order.rental_payment_token_id
        order.write({
            'rental_payment_token_id': payment_token.id,
            'rental_billing_address': full_billing,
            'rental_billing_postal_code': billing_postal_code,
        })

        if old_token:
            try:
                old_token.active = False
            except Exception:
                pass

        order.message_post(body=_(
            "Card on file updated by %s. New card: %s",
            cardholder_name,
            payment_token.payment_details or 'Card',
        ))

        try:
            self._send_card_reauthorization_confirmation(order, payment_token, cardholder_name)
        except Exception as e:
            _logger.error(
                "Card reauth confirmation email failed for %s: %s",
                order.name, e,
            )

        return {
            'success': True,
            'message': 'Card authorized successfully. Thank you!',
        }

    def _send_card_reauthorization_confirmation(self, order, token, cardholder_name):
        """Send confirmation email with card authorization details."""
        template = request.env.ref(
            'fusion_rental.mail_template_rental_card_reauth_confirmation',
            raise_if_not_found=False,
        )
        if template:
            template.sudo().with_context(
                cardholder_name=cardholder_name,
                card_details=token.payment_details or 'Card',
            ).send_mail(order.id, force_send=True)

    # =================================================================
    # Purchase Interest (from marketing email)
    # =================================================================

    @http.route(
        '/rental/purchase-interest/<int:order_id>/<string:token>',
        type='http',
        auth='public',
        website=True,
        methods=['GET'],
    )
    def rental_purchase_interest(self, order_id, token, **kwargs):
        """Handle customer expressing purchase interest from marketing email."""
        order = request.env['sale.order'].sudo().browse(order_id)
        if (
            not order.exists()
            or order.rental_agreement_token != token
        ):
            return request.render(
                'fusion_rental.cancellation_invalid_page',
                {'error': _("This link is invalid.")},
            )

        if not order.rental_purchase_interest:
            order.rental_purchase_interest = True
            order.activity_schedule(
                'mail.mail_activity_data_todo',
                date_deadline=fields.Date.today(),
                summary=_("Customer interested in purchasing rental product"),
                note=_(
                    "Customer %s expressed interest in purchasing the rental "
                    "product from order %s. Please follow up.",
                    order.partner_id.name, order.name,
                ),
                user_id=order.user_id.id or request.env.uid,
            )
            order.message_post(
                body=_("Customer expressed purchase interest via marketing email."),
            )

        return request.render(
            'fusion_rental.purchase_interest_success_page',
            {'order': order, 'partner': order.partner_id},
        )
