from odoo import api, fields, models, SUPERUSER_ID, registry
import random
import requests
from odoo.exceptions import UserError, ValidationError
import logging
from odoo.exceptions import AccessDenied

_logger = logging.getLogger(__name__)


class ResUsersInherit(models.Model):
    _inherit = 'res.users'

    otp_enabled = fields.Boolean(string="Enable OTP", default=False)
    otp_mobile_phone = fields.Char(string="Mobile Phone")
    otp_code = fields.Char(string="OTP Code", readonly=True)
    skip_otp = fields.Boolean(string="Skip OTP", default=False)

    def send_sms_to_user(self, body=None, phone=None):
        company = self.env.company
        # Check the SMS mode
        if company.sms_mode == 'test':
            _logger.info(f"--- SMS TEST MODE ---  for user {self.name} is {body}. SMS not sent.")
            return

        # --- Production Mode Logic ---
        # Validate that all configuration fields are set
        if not all([company.sms_api_url, company.sms_api_token, company.sms_sender_name]):
            raise ValidationError(
                "SMS API settings (URL, Token, Sender Name) are not fully configured in General Settings.")

        # SMS payload
        payload = {
            "recipients": [phone],
            "body": body,
            "sender": company.sms_sender_name,
        }

        # Headers for authentication
        headers = {
            "Authorization": f"Bearer {company.sms_api_token}",
            "Content-Type": "application/json",
        }

        # Send the SMS
        try:
            response = requests.post(company.sms_api_url, json=payload, headers=headers, timeout=10)
            response.raise_for_status()  # Raise an error for non-2xx responses
            _logger.info(f"{body} sent successfully to {phone}")
        except requests.exceptions.RequestException as e:
            _logger.error(f"Failed to send sms to {phone}: {e}")
            raise ValidationError("Failed to send sms. Please check API configuration or try again later.")

    def generate_otp(self):
        """
        Generate a random OTP, save it, and send it via SMS based on company settings.
        """
        self.ensure_one()
        if not self.otp_mobile_phone:
            raise ValidationError("No mobile phone number provided for OTP.")

        # Get the configuration from the user's current company
        company = self.env.company
        
        # Generate OTP regardless of mode
        if not self.skip_otp:
            otp = str(random.randint(1000, 9999))
        else:
            otp = '1111'
        self.otp_code = otp

        # Check the SMS mode
        if company.sms_mode == 'test':
            _logger.info(f"--- SMS TEST MODE --- OTP for user {self.name} is {otp}. SMS not sent.")
            # In test mode, we can return the OTP directly for testing purposes
            return otp

        # --- Production Mode Logic ---
        # Validate that all configuration fields are set
        if not all([company.sms_api_url, company.sms_api_token, company.sms_sender_name]):
            raise ValidationError("SMS API settings (URL, Token, Sender Name) are not fully configured in General Settings.")

        # SMS payload
        payload = {
            "recipients": [self.otp_mobile_phone],
            "body": f"Your OTP code is: {otp}",
            "sender": company.sms_sender_name,
        }

        # Headers for authentication
        headers = {
            "Authorization": f"Bearer {company.sms_api_token}",
            "Content-Type": "application/json",
        }

        # Send the SMS
        try:
            response = requests.post(company.sms_api_url, json=payload, headers=headers, timeout=10)
            response.raise_for_status()  # Raise an error for non-2xx responses
            _logger.info(f"OTP {otp} sent successfully to {self.otp_mobile_phone}")
        except requests.exceptions.RequestException as e:
            _logger.error(f"Failed to send OTP to {self.otp_mobile_phone}: {e}")
            raise ValidationError("Failed to send OTP. Please check API configuration or try again later.")

        return otp

    def validate_otp(self, otp):
            """Validate the OTP."""
            self.ensure_one()
            return self.otp_code == otp
    
    @classmethod
    def _login(cls, db, login, password, user_agent_env):
        try:
            # Try standard authentication first
            return super()._login(db, login, password, user_agent_env)
        except AccessDenied as e:
            with registry(db).cursor() as cr:
                cr.execute("SELECT id FROM res_users WHERE lower(login)=%s", (login.lower(),))
                res = cr.fetchone()
                
                if not res:
                    raise e  # User not found

                user_id = res[0]

                # Create environment to access models
                env = api.Environment(cr, SUPERUSER_ID, {})
                user = env['res.users'].browse(user_id)

                # Check if OTP is valid
                if user.validate_otp(password):  # OTP provided instead of password
                    return user.id  # Authenticate user by returning ID

                raise e  # OTP is incorrect
