import werkzeug
import logging
import requests
from werkzeug.urls import url_encode, url_decode
from odoo import http
from odoo.http import request
from odoo.addons.auth_oauth.controllers.main import OAuthLogin as OriginalOAuthLogin

_logger = logging.getLogger(__name__)


class OAuthLogin(OriginalOAuthLogin):
    
    def list_providers(self):
        """Override to use authorization code flow instead of implicit flow"""
        providers = super().list_providers()
        
        for provider in providers:
            if 'auth_link' in provider:
                auth_link = provider['auth_link']
                if 'response_type=token' in auth_link:
                    provider['auth_link'] = auth_link.replace('response_type=token', 'response_type=code')
                    _logger.info(f"Modified OAuth provider {provider.get('name')} to use authorization code flow")
        
        return providers
    
    @http.route('/auth_oauth/signin', type='http', auth='none')
    def signin(self, **kw):
        """Override signin to handle authorization code"""
        
        if 'code' in kw:
            _logger.info(f"OAuth callback received with code: {kw.get('code')[:10]}...")
            
            try:
                state = kw.get('state')
                if state:
                    import json
                    state_dict = json.loads(state)
                    provider_id = state_dict.get('p')
                else:
                    _logger.error("No state in OAuth callback")
                    return request.redirect('/web/login?oauth_error=2')
                
                provider = request.env['auth.oauth.provider'].sudo().browse(provider_id)
                if not provider:
                    _logger.error(f"Provider {provider_id} not found")
                    return request.redirect('/web/login?oauth_error=2')
                
                # Get issuer domain from validation_endpoint
                import re
                match = re.match(r'(https?://[^/]+)', provider.validation_endpoint)
                if not match:
                    _logger.error("Cannot extract issuer from validation_endpoint")
                    return request.redirect('/web/login?oauth_error=2')
                
                issuer = match.group(1)
                token_endpoint = f"{issuer}/application/o/token/"
                
                redirect_uri = request.httprequest.url_root.rstrip('/') + '/auth_oauth/signin'
                
                token_data = {
                    'grant_type': 'authorization_code',
                    'code': kw['code'],
                    'redirect_uri': redirect_uri,
                    'client_id': provider.client_id,
                    'client_secret': provider.client_secret,
                }
                
                _logger.info(f"Exchanging code at {token_endpoint}")
                _logger.info(f"Redirect URI: {redirect_uri}")
                
                response = requests.post(token_endpoint, data=token_data, timeout=10)
                _logger.info(f"Token response status: {response.status_code}")
                
                if response.status_code != 200:
                    _logger.error(f"Token exchange failed: {response.text}")
                    return request.redirect('/web/login?oauth_error=2')
                
                token_response = response.json()
                access_token = token_response.get('access_token')
                
                if not access_token:
                    _logger.error("No access token in response")
                    return request.redirect('/web/login?oauth_error=2')
                
                _logger.info(f"Successfully obtained access token: {access_token[:20]}...")
                
                kw['access_token'] = access_token
                kw.pop('code', None)
                
            except Exception as e:
                _logger.error(f"Failed to exchange code for token: {e}", exc_info=True)
                return request.redirect('/web/login?oauth_error=2')
        
        return super().signin(**kw)
