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

import logging
import pprint
from datetime import timedelta

from werkzeug.exceptions import Forbidden

from odoo import _, fields
from odoo.exceptions import ValidationError
from odoo.http import Controller, request, route


_logger = logging.getLogger(__name__)


class RazorpayController(Controller):

    OAUTH_RETURN_URL = '/payment/razorpay/oauth/return'

    @route(OAUTH_RETURN_URL, type='http', auth='user', methods=['GET'], website=True)
    def razorpay_return_from_authorization(self, **data):
        """ Exchange the authorization code for an access token and redirect to the provider form.

        :param dict data: The authorization code received from Razorpay, in addition to the provided
                          provider id and CSRF token that were sent back by the proxy.
        :raise Forbidden: If the received CSRF token cannot be verified.
        :raise ValidationError: If the provider id does not match any Razorpay provider.
        :return: Redirect to the payment provider form.
        """
        _logger.info("Returning from authorization with data:\n%s", pprint.pformat(data))

        # Retrieve the Razorpay data and Odoo metadata from the redirect data.
        provider_id = int(data['provider_id'])
        authorization_code = data.get('authorization_code')
        csrf_token = data['csrf_token']
        provider_sudo = request.env['payment.provider'].sudo().browse(provider_id).exists()
        if not provider_sudo or provider_sudo.code != 'razorpay':
            raise ValidationError(_("Could not find Razorpay provider with id %s", provider_sudo))

        # Verify the CSRF token.
        if not request.validate_csrf(csrf_token):
            _logger.warning("CSRF token verification failed.")
            raise Forbidden()

        # Request and set the OAuth tokens on the provider.
        action = request.env.ref('payment.action_payment_provider')
        redirect_url = f'/odoo/action-{action.id}/{int(provider_sudo.id)}'
        if not authorization_code:  # The user cancelled the authorization.
            return request.redirect(redirect_url)

        # Fetch an access token using the authorization token.
        proxy_payload = self.env['payment.provider']._prepare_json_rpc_payload(
            {'authorization_code': authorization_code}
        )
        try:
            response_content = provider_sudo._send_api_request(
                'POST', '/get_access_token', json=proxy_payload, is_proxy_request=True
            )
        except ValidationError as e:
            return request.render(
                'payment_razorpay.authorization_error',
                qcontext={'error_message': str(e), 'provider_url': redirect_url},
            )
        expires_in = fields.Datetime.now() + timedelta(seconds=int(response_content['expires_in']))
        provider_sudo.write({
            # Reset the classical API key fields.
            'razorpay_key_id': None,
            'razorpay_key_secret': None,
            'razorpay_webhook_secret': None,
            # Save the OAuth credentials.
            'razorpay_account_id': response_content['razorpay_account_id'],
            'razorpay_public_token': response_content['public_token'],
            'razorpay_refresh_token': response_content['refresh_token'],
            'razorpay_access_token': response_content['access_token'],
            'razorpay_access_token_expiry': expires_in,
            # Enable the provider.
            'state': 'enabled',
            'is_published': True,
        })
        try:
            provider_sudo.action_razorpay_create_webhook()
        except ValidationError as error:
            _logger.warning(error)
        return request.redirect(redirect_url)
