# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)

import logging
from datetime import timedelta
from odoo import http, fields
from odoo.http import request

_logger = logging.getLogger(__name__)

ITEMS_PER_PAGE = 50


class PortalInventory(http.Controller):

    # ── Core data builder ──

    def _get_inventory_data(self, search='', category_ids=None, page=1,
                            warehouse_filter=None):
        """Build inventory data merging local stock with remote sync data.
        Fetches all matching products, filters/sorts globally, then paginates
        so local in-stock items always appear first across all pages."""
        Product = request.env['product.product'].sudo()
        Booking = request.env['fusion.inventory.booking'].sudo()
        POLine = request.env['purchase.order.line'].sudo()
        Mapping = request.env['fusion.product.sync.mapping'].sudo()

        show_local = warehouse_filter in (None, '', 'all', 'local')
        show_remote = warehouse_filter in (None, '', 'all', 'remote')

        domain = [
            ('type', '!=', 'service'),
            ('is_storable', '=', True),
            ('active', '=', True),
        ]
        if search:
            domain += ['|', '|',
                        ('name', 'ilike', search),
                        ('default_code', 'ilike', search),
                        ('barcode', 'ilike', search)]
        if category_ids:
            domain.append(('categ_id', 'in', category_ids))

        all_products = Product.search(domain, order='name asc')

        all_product_ids = all_products.ids
        all_tmpl_ids = all_products.mapped('product_tmpl_id').ids

        bookings = Booking.search([
            ('product_id', 'in', all_product_ids),
            ('state', '=', 'active'),
        ])
        booked_map = {}
        for b in bookings:
            booked_map.setdefault(b.product_id.id, 0)
            booked_map[b.product_id.id] += b.quantity

        po_lines = POLine.search([
            ('product_id', 'in', all_product_ids),
            ('order_id.state', 'in', ('purchase', 'done')),
        ])
        shadow_map = {}
        for pl in po_lines:
            remaining = pl.product_qty - pl.qty_received
            if remaining > 0:
                shadow_map.setdefault(pl.product_id.id, 0)
                shadow_map[pl.product_id.id] += remaining

        remote_stock_map = {}
        remote_warehouses_map = {}
        if show_remote:
            mappings = Mapping.search([
                ('local_product_id', 'in', all_tmpl_ids),
                ('config_id.active', '=', True),
            ])
            for m in mappings:
                tmpl_id = m.local_product_id.id
                total_remote = m.remote_qty_available or 0
                remote_stock_map[tmpl_id] = (
                    remote_stock_map.get(tmpl_id, 0) + total_remote)

                wh_details = []
                for sl in m.stock_line_ids:
                    if sl.qty_available > 0:
                        wh_details.append({
                            'warehouse': sl.sync_warehouse_id.name,
                            'code': sl.sync_warehouse_id.code,
                            'company': sl.sync_warehouse_id.company_name,
                            'qty': sl.qty_available,
                        })
                if wh_details:
                    remote_warehouses_map.setdefault(tmpl_id, []).extend(
                        wh_details)

        all_rows = []
        for p in all_products:
            booked = booked_map.get(p.id, 0)
            shadow = shadow_map.get(p.id, 0)
            cost = p.standard_price or 0
            sale = p.lst_price or 0
            margin_pct = (
                ((sale - cost) / sale * 100) if sale > 0 and cost > 0 else 0)
            remote_qty = remote_stock_map.get(p.product_tmpl_id.id, 0)
            remote_whs = remote_warehouses_map.get(p.product_tmpl_id.id, [])
            local_qty = p.qty_available

            total_stock = local_qty + remote_qty + shadow
            if total_stock <= 0:
                continue

            if warehouse_filter == 'local' and local_qty <= 0:
                continue
            if warehouse_filter == 'remote' and remote_qty <= 0:
                continue

            all_rows.append({
                'id': p.id,
                'tmpl_id': p.product_tmpl_id.id,
                'name': p.name or '',
                'default_code': p.default_code or '',
                'category': p.categ_id.display_name if p.categ_id else '',
                'category_id': p.categ_id.id if p.categ_id else 0,
                'qty_on_hand': local_qty,
                'available_qty': max(local_qty - booked, 0),
                'booked_qty': booked,
                'shadow_qty': shadow,
                'remote_qty': round(remote_qty, 1) if show_remote else 0,
                'remote_warehouses': remote_whs if show_remote else [],
                'total_qty': round(
                    local_qty + (remote_qty if show_remote else 0), 1),
                'sale_price': round(sale, 2),
                'margin_pct': round(margin_pct, 1),
            })

        all_rows.sort(key=lambda r: (-r['qty_on_hand'], r['name']))

        total = len(all_rows)
        offset = (page - 1) * ITEMS_PER_PAGE
        page_rows = all_rows[offset:offset + ITEMS_PER_PAGE]

        remote_only_rows = self._get_remote_only_products(
            search, category_ids, all_tmpl_ids, warehouse_filter)
        remote_total = len(remote_only_rows)

        return page_rows, total, remote_only_rows, remote_total

    def _get_remote_only_products(self, search='', category_ids=None,
                                  exclude_tmpl_ids=None,
                                  warehouse_filter=None):
        """Return products that exist only on remote instances.
        Excluded when filter is 'local' or when a category filter is active
        (remote categories can't be matched to local category IDs)."""
        if warehouse_filter == 'local':
            return []

        Mapping = request.env['fusion.product.sync.mapping'].sudo()

        domain = [
            ('local_product_id', '=', False),
            ('config_id.active', '=', True),
            ('remote_qty_available', '>', 0),
        ]
        if search:
            domain.append(('remote_product_name', 'ilike', search))

        if category_ids:
            cat_names = request.env['product.category'].sudo().browse(
                category_ids).mapped('name')
            if cat_names:
                cat_domain = ['|'] * (len(cat_names) - 1)
                for cn in cat_names:
                    cat_domain.append(('remote_category', 'ilike', cn))
                domain += cat_domain
            else:
                return []

        mappings = Mapping.search(domain, limit=50)

        rows = []
        for m in mappings:
            wh_details = []
            for sl in m.stock_line_ids:
                if sl.qty_available > 0:
                    wh_details.append({
                        'warehouse': sl.sync_warehouse_id.name,
                        'code': sl.sync_warehouse_id.code,
                        'company': sl.sync_warehouse_id.company_name,
                        'qty': sl.qty_available,
                    })

            rows.append({
                'id': 0,
                'tmpl_id': 0,
                'name': m.remote_product_name or '',
                'default_code': m.remote_default_code or '',
                'category': m.remote_category or '',
                'category_id': 0,
                'qty_on_hand': 0,
                'available_qty': 0,
                'booked_qty': 0,
                'shadow_qty': 0,
                'remote_qty': round(m.remote_qty_available, 1),
                'remote_warehouses': wh_details,
                'total_qty': round(m.remote_qty_available, 1),
                'sale_price': round(m.remote_list_price or 0, 2),
                'margin_pct': 0,
                'remote_only': True,
                'config_id': m.config_id.id,
                'config_name': m.config_id.name,
            })

        return rows

    # ── Main page ──

    @http.route('/my/inventory', type='http', auth='user', website=True)
    def portal_inventory(self, search='', category=None, page=1,
                         warehouse=None, **kw):
        page = int(page)
        category_ids = None
        if category:
            try:
                category_ids = [int(c) for c in category.split(',') if c]
            except (ValueError, AttributeError):
                category_ids = None

        rows, total, remote_only, remote_total = self._get_inventory_data(
            search, category_ids, page, warehouse)

        categories = request.env['product.category'].sudo().search(
            [], order='name asc')

        total_pages = max(1, (total + ITEMS_PER_PAGE - 1) // ITEMS_PER_PAGE)

        has_sync = bool(request.env['fusion.sync.config'].sudo().search([
            ('active', '=', True), ('state', '=', 'connected'),
        ], limit=1))

        all_warehouses = self._get_all_warehouses()

        return request.render('fusion_inventory.portal_inventory_sheet', {
            'products': rows,
            'remote_only_products': remote_only,
            'categories': categories,
            'search': search,
            'selected_categories': category_ids or [],
            'page': page,
            'total_pages': total_pages,
            'total_products': total + remote_total,
            'page_name': 'fi_inventory',
            'has_sync': has_sync,
            'all_warehouses': all_warehouses,
            'warehouse_filter': warehouse or 'all',
        })

    # ── JSON-RPC endpoints ──

    @http.route('/my/inventory/data', type='json', auth='user')
    def portal_inventory_data(self, search='', category_ids=None, page=1,
                              warehouse=None):
        rows, total, remote_only, _ = self._get_inventory_data(
            search, category_ids, int(page), warehouse)
        total_pages = max(1, (total + ITEMS_PER_PAGE - 1) // ITEMS_PER_PAGE)
        return {
            'products': rows + remote_only,
            'total': total,
            'page': int(page),
            'total_pages': total_pages,
        }

    @http.route('/my/inventory/book', type='json', auth='user')
    def portal_inventory_book(self, product_id, quantity=1):
        product_id = int(product_id)
        quantity = float(quantity)

        product = request.env['product.product'].sudo().browse(product_id)
        if not product.exists():
            return {'error': 'Product not found'}

        Booking = request.env['fusion.inventory.booking'].sudo()

        existing = Booking.search([
            ('product_id', '=', product_id),
            ('user_id', '=', request.env.uid),
            ('state', '=', 'active'),
        ], limit=1)
        if existing:
            return {'error': 'You already have an active booking for this product',
                    'booking_id': existing.id}

        hold_hours = int(request.env['ir.config_parameter'].sudo().get_param(
            'fusion_inventory.booking_hold_hours', '24') or 24)

        booking = Booking.create({
            'product_id': product_id,
            'user_id': request.env.uid,
            'quantity': quantity,
            'expiry_datetime': fields.Datetime.now() + timedelta(hours=hold_hours),
            'state': 'active',
        })

        return {
            'success': True,
            'booking_id': booking.id,
            'expires_at': fields.Datetime.to_string(booking.expiry_datetime),
        }

    @http.route('/my/inventory/release', type='json', auth='user')
    def portal_inventory_release(self, booking_id):
        booking = request.env['fusion.inventory.booking'].sudo().browse(int(booking_id))
        if not booking.exists() or booking.user_id.id != request.env.uid:
            return {'error': 'Booking not found'}
        booking.action_release()
        return {'success': True}

    @http.route('/my/inventory/categories', type='json', auth='user')
    def portal_inventory_categories(self, search=''):
        domain = []
        if search:
            domain.append(('name', 'ilike', search))
        categories = request.env['product.category'].sudo().search(
            domain, order='name asc', limit=100)
        return [{'id': c.id, 'name': c.display_name} for c in categories]

    @http.route('/my/inventory/warehouses', type='json', auth='user')
    def portal_inventory_warehouses(self):
        return self._get_all_warehouses()

    @http.route('/my/inventory/transfer', type='json', auth='user')
    def portal_inventory_transfer(self, product_id, quantity=1, config_id=None):
        """Create and execute an inter-company transfer from the portal."""
        product_id = int(product_id)
        quantity = float(quantity)

        product = request.env['product.product'].sudo().browse(product_id)
        if not product.exists():
            return {'error': 'Product not found'}

        if config_id:
            config = request.env['fusion.sync.config'].sudo().browse(int(config_id))
        else:
            config = request.env['fusion.sync.config'].sudo().search([
                ('active', '=', True),
                ('state', '=', 'connected'),
            ], limit=1)

        if not config:
            return {'error': 'No sync connection configured'}

        Mapping = request.env['fusion.product.sync.mapping'].sudo()
        mapping = Mapping.search([
            ('config_id', '=', config.id),
            ('local_product_id', '=', product.product_tmpl_id.id),
        ], limit=1)

        if not mapping:
            return {'error': f'Product not mapped on {config.name}. Run sync first.'}

        if mapping.remote_qty_available < quantity:
            return {
                'error': f'Insufficient remote stock. Available: {mapping.remote_qty_available}',
            }

        Transfer = request.env['fusion.inter.company.transfer'].sudo()
        transfer = Transfer.create({
            'config_id': config.id,
            'product_id': product_id,
            'quantity': quantity,
            'requested_by': request.env.uid,
            'notes': 'Created from portal inventory sheet.',
        })

        try:
            transfer.action_execute_transfer()
        except Exception as e:
            _logger.error('Portal transfer failed: %s', e)
            return {
                'error': f'Transfer failed: {str(e)}',
                'transfer_id': transfer.id,
            }

        return {
            'success': True,
            'transfer_id': transfer.id,
            'state': transfer.state,
            'local_po': transfer.local_po_id.name if transfer.local_po_id else None,
            'remote_so': transfer.remote_so_name,
        }

    # ── Helpers ──

    def _get_all_warehouses(self):
        """Return local (current company only) + remote warehouses for filter."""
        warehouses = []

        user_company = request.env.company
        local_whs = request.env['stock.warehouse'].sudo().search([
            ('company_id', '=', user_company.id),
        ])
        for wh in local_whs:
            warehouses.append({
                'id': f'local_{wh.id}',
                'name': wh.name,
                'code': wh.code,
                'type': 'local',
                'company': wh.company_id.name,
            })

        remote_whs = request.env['fusion.sync.warehouse'].sudo().search([
            ('active', '=', True),
        ])
        for wh in remote_whs:
            warehouses.append({
                'id': f'remote_{wh.id}',
                'name': wh.name,
                'code': wh.code,
                'type': 'remote',
                'company': wh.company_name,
            })

        return warehouses
