from odoo import models, fields, api, _
from odoo.exceptions import UserError
import datetime
import logging

_logger = logging.getLogger(__name__)

class PosOrder(models.Model):
    _inherit = 'pos.order'

    is_uber_order = fields.Boolean(string='Is Uber Order', default=False)
    uber_order_id = fields.Char(string='Uber Order ID')
    uber_delivery_id = fields.Char(string='Uber Delivery ID')
    uber_status = fields.Selection([
        ('pending', 'Pending Uber Pickup'),
        ('pickup', 'Uber Driver Picked Up'),
        ('delivering', 'In Transit'),
        ('delivered', 'Delivered'),
        ('cancelled', 'Cancelled')
    ], string='Uber Delivery Status')
    
    delivery_type = fields.Selection([
        ('none', 'None'),
        ('dine_in', 'Dine In'),
        ('takeaway', 'Takeaway'),
        ('uber', 'Uber Direct')
    ], string='Delivery Type', default='none')

    # Advanced Features Fields
    uber_tracking_url = fields.Char(string='Driver Tracking Link')
    uber_eta = fields.Datetime(string='Predicted Delivery Time')
    uber_delivery_fee = fields.Float(string='Uber Delivery Fee', readonly=True)
    uber_request_time = fields.Datetime(string='Uber Request Time')
    uber_alert_triggered = fields.Boolean(string='Driver Timeout Alert Sent', default=False)

    def _check_all_lines_ready(self):
        """Check if all kitchen items in the order are ready or served"""
        self.ensure_one()
        kitchen_lines = self.lines.filtered(lambda l: l.product_id.is_kitchen_item)
        if not kitchen_lines:
            return False
        return all(line.preparation_status in ['ready', 'served'] for line in kitchen_lines)

    def action_request_uber_delivery(self):
        """Trigger Uber Direct delivery request via API"""
        # Ensure imports are available inside method if not global (but better global)
        # Adding imports here for safety, though cleaner at top
        import requests 
        import json
        
        for order in self:
            if order.is_uber_order and order.uber_status and order.uber_status != 'cancelled':
                continue
            
            # 1. Get Configuration
            config = self.env['uber.config'].search([('active', '=', True)], limit=1)
            if not config:
                raise UserError(_("Uber Integration is not configured. Please check Settings."))

            customer_id = config.customer_id
            if not customer_id:
                raise UserError(_("Uber Customer ID is missing in configuration."))

            # 2. Get Partner (Customer)
            partner = order.partner_id
            if not partner:
                raise UserError(_("Customer is required for Uber delivery."))
            if not partner.street or not partner.city or not partner.zip:
                raise UserError(_("Customer address is incomplete (Street, City, Zip required)."))

            # 3. Authenticate
            try:
                access_token = config._get_access_token()
            except Exception as e:
                raise UserError(_("Authentication Failed: %s") % str(e))

            # 4. Prepare Payload
            company = order.company_id
            # Pickup Location (Restaurant)
            pickup_address = json.dumps({
                "street_address": [company.street],
                "city": company.city,
                "state": company.state_id.code or "",
                "zip_code": company.zip,
                "country": company.country_id.code or "US"
            })
            
            # Dropoff (Customer)
            dropoff_address = json.dumps({
                "street_address": [partner.street],
                "city": partner.city,
                "state": partner.state_id.code or "",
                "zip_code": partner.zip,
                "country": partner.country_id.code or "US"
            })

            items = []
            for line in order.lines:
                if not line.product_id.is_kitchen_item: # Optional filter
                    continue
                items.append({
                    "name": line.full_product_name or line.product_id.name,
                    "quantity": int(line.qty),
                    "price": int(line.price_unit * 100), # Cents
                    "currency_code": order.currency_id.name
                })
            
            if not items:
                # Fallback if no kitchen items found to at least send something
                items.append({"name": "Food Order", "quantity": 1, "price": int(order.amount_total * 100), "currency_code": order.currency_id.name})

            payload = {
                "pickup_name": company.name,
                "pickup_address": pickup_address,
                "pickup_phone_number": company.phone or "+15555555555",
                "dropoff_name": partner.name,
                "dropoff_address": dropoff_address,
                "dropoff_phone_number": partner.phone or partner.mobile or "+15555555555",
                "manifest_items": items,
                "test_specifications": {"robo_courier_specification": {"mode": "auto"}} if config.environment == 'sandbox' else None
            }

            # 5. Call API
            api_url = f"https://api.uber.com/v1/customers/{customer_id}/deliveries"
            headers = {
                'Authorization': f'Bearer {access_token}',
                'Content-Type': 'application/json'
            }

            try:
                # Note: Sending the request directly to create delivery
                response = requests.post(api_url, headers=headers, json=payload)
                response.raise_for_status()
                data = response.json()
                
                # 6. Process Success
                # Uber API returns fee as integer (cents) usually? Need to check. 
                # Docs say 'fee' object with 'amount'
                # Assuming 'fee' field in response is float or int. 
                # Careful: Uber often returns amounts in minor units or currency formatted.
                # Standard response has `fee` integer? Let's assume standard float from JSON if parsed, or check specific field.
                # Actually, check `fee` in response.
                
                delivery_fee = 0.0
                if 'fee' in data:
                    # Fee is in cents (minor units), convert to major units
                    delivery_fee = float(data['fee']) / 100.0

                order.write({
                    'uber_status': 'pending',
                    'is_uber_order': True,
                    'uber_delivery_id': data.get('id'),
                    'uber_request_time': fields.Datetime.now(),
                    'uber_delivery_fee': delivery_fee,
                    'uber_tracking_url': data.get('tracking_url'),
                    'uber_eta': fields.Datetime.now() + datetime.timedelta(minutes=30) # Ideally parse `estimated_dropoff_time`
                })

                # Add charge to bill
                if delivery_fee > 0:
                    order._add_uber_delivery_fee(delivery_fee)

            except requests.exceptions.HTTPError as e:
                # Log the raw response so we can see which parameter is invalid
                _logger.error("Uber Direct Raw Error Response (%s): %s", e.response.status_code, e.response.text)
                # Try to parse the error message if it's JSON
                try:
                    err_data = e.response.json()
                    err_code = err_data.get('code', 'unknown_error')
                    err_msg = err_data.get('message', 'An error occurred with Uber API.')
                    
                    if err_code == 'address_undeliverable':
                        # Special handling for radius errors (most common issue)
                        details = err_data.get('metadata', {}).get('details', '')
                        raise UserError(_("Address Undeliverable: The drop-off location is outside Uber's delivery radius. \n\nDetails: %s") % details)
                    
                    raise UserError(_("Uber API Error (%s): %s") % (err_code, err_msg))
                except (ValueError, AttributeError):
                    # Fallback to default error text if not JSON
                    raise UserError(_("Uber API Error %s: %s") % (e.response.status_code, e.response.text))
            except Exception as e:
                raise UserError(_("Failed to request delivery: %s") % str(e))

    def _add_uber_delivery_fee(self, amount):
        """Add the delivery fee as a line item if not already added"""
        config = self.env['uber.config'].search([('active', '=', True)], limit=1)
        if config and config.delivery_product_id:
            # Check if fee line exists
            fee_line = self.lines.filtered(lambda l: l.product_id == config.delivery_product_id)
            if not fee_line:
                taxes = config.delivery_product_id.taxes_id.compute_all(amount, self.pricelist_id.currency_id, 1, product=config.delivery_product_id, partner=self.partner_id)
                self.write({'lines': [(0, 0, {
                    'product_id': config.delivery_product_id.id,
                    'full_product_name': config.delivery_product_id.name,
                    'price_unit': amount,
                    'qty': 1,
                    'tax_ids': [(6, 0, config.delivery_product_id.taxes_id.ids)],
                    'price_subtotal': taxes['total_excluded'],
                    'price_subtotal_incl': taxes['total_included'],
                })]})

    def action_cancel_uber_delivery(self):
        for order in self:
            if not order.uber_delivery_id:
                continue
            order.write({
                'uber_status': 'cancelled',
                'uber_delivery_id': False,
                'is_uber_order': False,
                'uber_tracking_url': False,
                'uber_eta': False
            })
            # order.message_post(body="Uber Direct delivery request cancelled.")

    def action_sync_uber_status(self):
        """Fetch latest status from Uber API and update POS order"""
        import requests
        config = self.env['uber.config'].search([('active', '=', True)], limit=1)
        if not config or not config.customer_id:
            return
            
        access_token = config._get_access_token()
        headers = {'Authorization': f'Bearer {access_token}'}
        
        for order in self:
            if not order.uber_delivery_id:
                continue
                
            try:
                api_url = f"https://api.uber.com/v1/customers/{config.customer_id}/deliveries/{order.uber_delivery_id}"
                response = requests.get(api_url, headers=headers)
                if response.status_code == 200:
                    data = response.json()
                    _logger.info("Uber Status Raw Data for %s: %s", order.name, data)
                    status_map = {
                        'pending': 'pending',
                        'pickup': 'pickup',
                        'pickup_complete': 'delivering',
                        'delivery_complete': 'delivered',
                        'delivered': 'delivered',
                        'cancelled': 'cancelled'
                    }
                    new_status = status_map.get(data.get('status'), order.uber_status)
                    
                    vals = {'uber_status': new_status}
                    # If status progressed beyond pending, HIDE the alert
                    if new_status != 'pending':
                        vals['uber_alert_triggered'] = False
                        
                    if new_status != order.uber_status:
                        # Send signal to UI to refresh the order form
                        self.env['bus.bus']._sendone('uber_status_updates', 'status_changed', {
                            'order_id': order.id,
                            'new_status': new_status
                        })
                        
                    order.write(vals)
                    _logger.info("Uber Status Synced for %s: %s", order.name, new_status)
            except Exception as e:
                _logger.error("Failed to sync Uber status for %s: %s", order.name, str(e))

    @api.model
    def cron_check_uber_driver_assignment(self):
        """Auto-alert and status update cron"""
        config = self.env['uber.config'].search([('active', '=', True)], limit=1)
        if not config:
            return

        # 1. Sync status for all active orders
        active_orders = self.search([('uber_status', 'in', ['pending', 'pickup', 'delivering'])])
        active_orders.action_sync_uber_status()

        # 2. Trigger alerts for those still stuck in pending
        if config.timeout_minutes > 0:
            timeout_threshold = fields.Datetime.now() - datetime.timedelta(minutes=config.timeout_minutes)
            pending_orders = self.search([
                ('uber_status', '=', 'pending'),
                ('uber_request_time', '<=', timeout_threshold),
                ('uber_alert_triggered', '=', False)
            ])

            for order in pending_orders:
                order.uber_alert_triggered = True
                self.env['bus.bus']._sendone('pos_alerts', 'uber_timeout', {
                    'order_name': order.name,
                    'minutes': config.timeout_minutes
                })

    def action_view_uber_map(self):
        """Open Uber Live Tracking Link"""
        self.ensure_one()
        if not self.uber_tracking_url:
            raise UserError(_("No tracking link available yet."))
        return {
            'type': 'ir.actions.act_url',
            'url': self.uber_tracking_url,
            'target': 'new',
        }
