my user creation handled here from dotenv import load_dotenv

load_dotenv()
from odoo import models, fields, api, _
import requests
from datetime import datetime, timedelta, timezone
import os
from dotenv import load_dotenv
import random
import string
from io import BytesIO
import base64
import hashlib
from odoo.addons.fastapi_addon.utils.redis_session import redis_session_manager
import jwt
import logging
load_dotenv()

_logger = logging.getLogger(__name__)

class User(models.Model):
    _inherit = 'res.partner'

    SECRET_KEY = os.getenv(
        "SECRET_KEY", "f4caa6cf36b77e8d3r4"
            )
    ALGORITHM = os.getenv("ALGORITHM", "HS256")
    CLIENT_ID = os.getenv('COMMETA_CLIENT_ID')
    CLIENT_SECRET = os.getenv("COMMETA_CLIENT_SECRET")
    ACCESS_TOKEN_EXPIRE_MINUTES = os.getenv('ACCESS_TOKEN_EXPIRE_MINUTES')
    REFRESH_TOKEN_EXPIRE_MINUTES = os.getenv("REFRESH_TOKEN_EXPIRE_MINUTES")

    avatar = fields.Binary(string=_('Avatar'), attachment=True)
    phone_number = fields.Char(string=_('telefon raqam'))
    passport_number = fields.Char(string=_('passport raqam'))
    language = fields.Selection([('uzbek', _('uzbek')), ('russian', _('rus')), ('english', _('english'))])
    active = fields.Boolean(string=_('Faol'), default=True)
    pnfl = fields.Char(string=_('pnfl'))
    home_ids = fields.One2many('user.home.session', "partner_id", string=_('Home'), required=False)
    mahalla_id = fields.Char(string=_('Mahalla ID'), compute='_compute_mahalla_id', store=True, invisible=True)

    @api.depends('home_ids')
    def _compute_mahalla_id(self):
        for partner in self:
            main_home = partner.home_ids.filtered(lambda home: home.is_main_home)
            if main_home:
                partner.mahalla_id = main_home.home_id.mahalla_id.id

    first_name = fields.Char(string=_('First Name'))
    last_name = fields.Char(string=_('Last Name'))
    middle_name = fields.Char(string=_('Middle Name'))
    gender = fields.Selection([('male', _('Male')), ('female', _('Female'))])
    birth_date = fields.Date(string='Birth Date')

    gift = fields.Many2many('gift', string=_("sovg'alar"))
    last_login = fields.Datetime(string=_("Last Login"))
    last_activity = fields.Datetime(string=_('Last Activity'))

    notifications_enabled = fields.Boolean(
        string=_('Enable Notifications'),
        default=True,
        help=_("Enable or disable all push notifications for this user")
    )

    # Timetable Data
    timetable_avatar = fields.Char(string=_('Avatar From Timetable'))
    job = fields.Char(string=_('Job From Timetable'))
    timetable_full_name = fields.Char(string=_('Full Name Timetable'))
    area = fields.Char(string=_('Timetable Area'))

    is_mahalla_member = fields.Boolean(string=_('Is Mahalla Member'), default=True)

    # Fields for Commeta OAuth
    commeta_access_token = fields.Char(string=_('Commeta Access Token'))
    commeta_refresh_token = fields.Char(string=_('Commeta Refresh Token'))
    commeta_token_expires_at = fields.Datetime(string=_('Commeta Token Expires At'))

    name = fields.Char(string=_('Name'), compute='_compute_name', inverse='_inverse_name', store=True)
    has_pnfl = fields.Boolean(string=_('Has PNFL'), compute='_compute_has_pnfl', store=True,  readonly=True)

    def get_full_name(self):
        """Retrieve first, last, and middle names if they exist."""
        name_parts = []
        if self.first_name:
            name_parts.append(self.first_name)
        if self.last_name:
            name_parts.append(self.last_name)
        if self.middle_name:
            name_parts.append(self.middle_name)

        return ' '.join(name_parts) if name_parts else None

    def get_masked_name(self):
        """Retrieve name with masked initials for middle and last names if they exist."""
        name_parts = []
        if self.first_name:
            name_parts.append(self.first_name)
        if self.last_name:
            masked_last = f"{self.last_name[0]}{'*' * (len(self.last_name) - 1)}"
            name_parts.append(masked_last)
        if self.middle_name:
            masked_middle = ' '.join(
                f"{word[0]}{'*' * (len(word) - 1)}" for word in self.middle_name.split()
            )
            name_parts.append(masked_middle)

        return ' '.join(name_parts) if name_parts else None

    @api.depends('pnfl')
    def _compute_has_pnfl(self):
        for partner in self:
            return bool(partner.pnfl)

    @api.depends('first_name', 'middle_name', 'last_name')
    def _compute_name(self):
        for partner in self:
            if not partner.name:
                # Compute name only if it's not manually set
                names = [partner.first_name, partner.last_name, partner.middle_name]
                partner.name = ' '.join(filter(None, names))

    def _inverse_name(self):
        """This method allows manual override of the name field."""
        # If the user sets the 'name' manually, this method allows saving it directly.
        for partner in self:
            partner.name = partner.name

    def _fetch_and_create_homes(self):
        if not self.pnfl:
            return False

        headers = {
            "Authorization": "Bearer U86mEBBLa7vbQ5ljA0xob",
            "mahalla-secret-key": "acx8TvN7xb7w=",
            "Content-Type": "application/json",
        }
        payload = {"pinfl": self.pnfl}

        try:
            response = requests.post(
                "https://mahalla.ijro.uz/api/mahalla-integration/house-by-citizen-pinfl",
                headers=headers,
                json=payload,
                timeout=10
            )
            response.raise_for_status()
            data = response.json()

            if not data.get("success") or not data.get("response"):
                _logger.error("Failed to fetch and create homes for user: %s", self.pnfl)
                return False

            for home_data in data["response"]:
                if not home_data.get("cadastre"):
                    _logger.error("Cadastre not found: %s", home_data)
                    continue

                region = self.env['region'].sudo().search([('soato', '=', home_data['region']['soato'])], limit=1)
                if not region:
                    region = self.env['region'].sudo().create({
                        'name': home_data['region']['name'],
                        'soato': home_data['region']['soato']
                    })

                district = self.env['district'].sudo().search([('soato', '=', home_data['district']['soato'])], limit=1)
                if not district:
                    district = self.env['district'].sudo().create({
                        'name': home_data['district']['name'],
                        'soato': home_data['district']['soato'],
                        'region_id': region.id
                    })

                mahalla = self.env['mahalla'].sudo().search([('stir', '=', home_data['mahalla']['stir'])], limit=1)
                if not mahalla:
                    mahalla = self.env['mahalla'].sudo().create({
                        'name': home_data['mahalla']['name'],
                        'stir': home_data['mahalla']['stir'],
                        'district_id': district.id
                    })

                existing_home = self.env['home'].sudo().search([
                    ('cadastral_number', '=', home_data['cadastre'])
                ], limit=1)

                if not existing_home:
                    home = self.env['home'].sudo().create({
                        'partner_id': self.id,
                        'name': home_data['street'],
                        'street': f"{home_data['street']} ko'chasi, {home_data['house_number']} uy",
                        'home_info': f"{region.name}, {district.name}, {mahalla.name}",
                        'cadastral_number': home_data['cadastre'],
                        'area': 0.0,
                        'living_space': 0.0,
                        'region_id': region.id,
                        'district_id': district.id,
                        'mahalla_id': mahalla.id
                    })
                else:
                    home = existing_home

                existing_session = self.env['user.home.session'].sudo().search([
                    ('partner_id', '=', self.id),
                    ('home_id', '=', home.id),
                ], limit=1)

                if not existing_session:
                    main_home_count = self.env['user.home.session'].sudo().search_count([
                        ('partner_id', '=', self.id),
                        ('is_main_home', '=', True)
                    ])
                    self.env['user.home.session'].sudo().create({
                        'home_name': home_data['street'],
                        'partner_id': self.id,
                        'home_id': home.id,
                        'is_owner': home_data.get('is_owner', False),
                        'is_main_home': not bool(main_home_count)
                    })

            return True

        except requests.RequestException:
            return False

    @api.model
    def create(self, vals):
        partner = super(User, self).create(vals)
        if partner.pnfl:
            partner._fetch_and_create_homes()
        return partner

    def write(self, vals):
        result = super(User, self).write(vals)
        if 'pnfl' in vals and vals['pnfl']:
            for partner in self:
                partner._fetch_and_create_homes()
        return result

    def generate_code_verifier(self):
        return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(43, 128)))

    def generate_code_challenge(self, code_verifier):
        code_challenge = hashlib.sha256(code_verifier.encode('utf-8')).digest()
        return base64.urlsafe_b64encode(code_challenge).decode('utf-8').replace('=', '')

    @api.model
    def generate_commeta_oauth_link(self):
        code_verifier = self.generate_code_verifier()
        code_challenge = self.generate_code_challenge(code_verifier)

        url = f"https://id.commeta.uz/oauth?client_id={self.allenge={code_challenge}&response_type=code&platform=mobile"

        return {'url': url, 'code_verifier': code_verifier}

    @api.model
    def commeta_exchange_code(self, code, code_verifier):
        import json

        TOKEN_ENDPOINT = "https://comtoken/"

        token_params = {
            "grant_type": "authorization_code",
            "client_id": self.CLIENT_ID,
            "client_secret": self.CLIENT_SECRET,
            "code": code,
            'redirect_uri': "https://mahallam-backend.uicgroup.tech/api/auth/callback",
            'code_verifier': code_verifier
        }

        response = requests.post(TOKEN_ENDPOINT, data=token_params)

        if response.status_code == 200:
            return json.loads(response.content.decode('utf-8'))
        return None

    @api.model
    def commeta_get_user_info(self, access_token):
        # USER_INFO_ENDPOINT = "https://cfgasffaommefadta.uz/ths/UserDetail/"
        USER_INFO_ENDPOINT = "https://th/UserInfoOfficial/"
        headers = {"Authorization": f"Bearer {access_token}"}

        response = requests.get(USER_INFO_ENDPOINT, headers=headers)
        if response.status_code == 200:
            print(response.json(), 'This is Commeta Response')
            return response.json()
        return None

    def create_tokens(self, data: dict):
        SECRET_KEY = os.getenv(
            "SECRET_KEY", "10d25e094faa6ca2556c810155b7a9563b39f7099f6f0f4caa6cf36b77e8d3r4"
        )
        ALGORITHM = os.getenv("ALGORITHM", "HS256")

        
        access_token_expire = datetime.now(timezone.utc) + timedelta(
            minutes=36000
        )
        refresh_token_expire = datetime.now(timezone.utc) + timedelta(
            days=365
        )

        access_token_data = data.copy()
        access_token_data.update({"exp": access_token_expire, "token_type": "access"})

        refresh_token_data = data.copy()
        refresh_token_data.update(
            {"exp": refresh_token_expire, "token_type": "refresh"}
        )

        access_token = jwt.encode(access_token_data, self.SECRET_KEY, self.ALGORITHM)
        refresh_token = jwt.encode(refresh_token_data, self.SECRET_KEY, self.ALGORITHM)

        return access_token, refresh_token

    @api.model
    def commeta_authenticate(self, token_data, device_data):
        if not token_data:
            return {'error': 'No token data provided'}

        user_info = self.commeta_get_user_info(token_data['access_token'])
        if not user_info:
            return {'error': 'Failed to get user info'}



        user = self.sudo().search([('phone_number', '=', user_info['phone'])], limit=1)
        profile = user_info['profile']

        avatar_data = None
        if 'avatar' in profile and profile['avatar'] and (not user or not user.avatar):
            try:
                response = requests.get(profile['avatar'])
                if response.status_code == 200:
                    avatar_data = base64.b64encode(BytesIO(response.content).read()).decode('utf-8')
            except requests.RequestException as e:
                _logger.error(f"Failed to download avatar: {e}")

        if not user:
            # Create a new user if none exists
            user_data = {
                'first_name': profile['first_name'],
                'name': profile['first_name'],
                'last_name': profile['last_name'],
                'middle_name': profile['middle_name'],
                'phone_number': user_info['phone'],
                'birth_date': profile['birth_date'],
                # 'avatar': profile['avatar'],  # TODO avatar for user
                'gender': 'male' if profile['gender'] == 'M' else 'female',
                'last_login': datetime.now(),
                'pnfl': profile['pinfl'],
            }
            if avatar_data:
                user_data['avatar'] = avatar_data
            user = self.sudo().create(user_data)
        else:
            # Update only missing or outdated fields
            profile = user_info['profile']
            update_fields = {}

            try:
                if not user.first_name:
                    update_fields['first_name'] = profile['first_name']
                    update_fields['name'] = profile['first_name']

                if not user.last_name:
                    update_fields['last_name'] = profile['last_name']

                if not user.middle_name:
                    update_fields['middle_name'] = profile['middle_name']

                if not user.birth_date:
                    update_fields['birth_date'] = profile['birth_date']

                if not user.avatar and avatar_data:
                    update_fields['avatar'] = avatar_data # TODO Avatar For user

                if not user.gender:
                    update_fields['gender'] = 'male' if profile['gender'] == 'M' else 'female'

                if not user.pnfl:
                    update_fields['pnfl'] = profile['pinfl']
            except KeyError as e:
                return {'error': f"Missing required field in profile: {e}"}

            if update_fields:
                update_fields['last_login'] = datetime.now()  # Update the last login time
                user.write(update_fields)

        device_user = self.env['user.device'].sudo().search([
            ('partner_id', '=', user.id),
            ('device_name', '=', device_data.get('device_name')),
            ('device_unique_name', '=', device_data.get('device_unique_name')),
        ])
        if not device_user:
            device_user = self.env['user.device'].sudo().create({
                'partner_id': user.id,
                'device_name': device_data.get('device_name'),
                'device_unique_name': device_data.get('device_unique_name'),
                'is_ios': device_data.get('is_ios'),
            })

        existing_sessions = redis_session_manager.get_sessions_by_user_and_device(user.id, device_user.id)

        if existing_sessions:
            session_id = existing_sessions[0]  # Assuming sessions are stored as a list
            session_data = redis_session_manager.get_session(session_id)
        else:
            # Create a new session if no existing session is found
            user_data = {
                'user_id': user.id,
                'expires_at': datetime.now(timezone.utc) + timedelta(days=365),
                'device_name': device_data.get('device_name'),
                'device_id': device_user.id,
                'device_unique_name': device_data.get('device_unique_name'),
                'is_ios': device_data.get('is_ios'),
            }
            session_id = redis_session_manager.create_session(user_data=user_data)

        project_token_data = {
            'user_id': user.id,
            'phone': user.phone_number,
            'device_user_id': device_user.id,
            'session_id': session_id
        }
        access_token, refresh_token = self.create_tokens(project_token_data)

        access_token_expire = datetime.now(timezone.utc) + timedelta(days=365)

        return {
            'user_id': user.id,
            "username": user.name,
            'access_token': access_token,
            'refresh_token': refresh_token,
            'expires_at': access_token_expire.isoformat(),
            'is_verified': bool(user.pnfl),
        }


    @api.model
    def create_user_in_timetable(self):
        users = self.env['res.partner'].sudo().search([
            ('timetable_avatar', '=', False),
            ('job', '=', False),
            ('is_mahalla_member', '=', True),
            ('pnfl', '!=', False)
        ])



        if not users:
            return None


        for user in users:
            url = 'https://tabel-mahalla.uz/user/mahalla/create'

            # Constructing the payload with the user data
            payload = {
                "first_name": user.first_name,
                "last_name": user.last_name,
                "middle_name": user.middle_name,
                "gender": user.gender,
                "birth_date": user.birth_date.isoformat() if user.birth_date else None,
                "phone_number": user.phone_number,
                "passport_number": user.passport_number,
                "language": user.language,
                "active": user.active,
                "pnfl": user.pnfl,
                # "avatar": user.avatar.decode('utf-8') if user.avatar else None,  # Assuming avatar is binary
                "job": user.job,
                "timetable_avatar": user.timetable_avatar,
                "is_mahalla_member": user.is_mahalla_member,
                "last_login": user.last_login.isoformat() if user.last_login else None,
                "last_activity": user.last_activity.isoformat() if user.last_activity else None,
            }

            headers = {
                'Content-Type': 'application/json'
            }

            response = requests.post(url=url, json=payload, headers=headers)

            try:
                if response.status_code == 200:
                    # Try parsing JSON content
                    data = response.json()
                    print("Parsed JSON response:", data)

                    if data:
                        data = data.get('data')
                        if data:
                            user.job = data.get('job')
                            user.timetable_avatar = data.get('image')
                    else:
                        print("No 'data' field found in JSON response.")
                else:
                    print(f"Unexpected response status: {response.status_code}")
                    user.is_mahalla_member = False
            except requests.exceptions.JSONDecodeError:
                print("Failed to decode JSON from response.")
                print("Response text:", response.text)  # Log the raw response text for inspection
                user.is_mahalla_member = False