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

import logging
import re
from odoo import models, fields, api

_logger = logging.getLogger(__name__)


class FusionInventoryDiscrepancy(models.Model):
    _name = 'fusion.inventory.discrepancy'
    _description = 'Inventory Discrepancy'
    _inherit = ['mail.thread']
    _order = 'scan_date desc'
    _rec_name = 'display_name'

    display_name = fields.Char(compute='_compute_display_name')
    product_id = fields.Many2one(
        'product.product', string='Product',
        required=True, ondelete='cascade', index=True)
    expected_qty = fields.Float(string='Expected Quantity')
    actual_qty = fields.Float(string='Actual Quantity')
    difference = fields.Float(
        string='Difference', compute='_compute_difference', store=True)
    discrepancy_type = fields.Selection([
        ('qty_mismatch', 'Quantity Mismatch'),
        ('missing_serial', 'Missing Serial Number'),
        ('orphan_serial', 'Orphaned Serial (no quant)'),
        ('untracked_serial', 'Serial in Notes (not in system)'),
    ], string='Type', required=True, index=True)
    missing_serials = fields.Text(
        string='Serial Numbers',
        help='Serial numbers that were found/missing')
    source = fields.Char(
        string='Source',
        help='Where the discrepancy was detected')
    state = fields.Selection([
        ('detected', 'Detected'),
        ('reviewed', 'Reviewed'),
        ('resolved', 'Resolved'),
        ('ignored', 'Ignored'),
    ], string='Status', default='detected', required=True,
        tracking=True, index=True)
    scan_date = fields.Datetime(
        string='Scan Date', default=fields.Datetime.now, required=True)
    reviewed_by = fields.Many2one('res.users', string='Reviewed By')
    resolution_notes = fields.Text(string='Resolution Notes')

    @api.depends('product_id', 'discrepancy_type')
    def _compute_display_name(self):
        for rec in self:
            product = rec.product_id.name or 'Unknown'
            dtype = dict(rec._fields['discrepancy_type'].selection).get(
                rec.discrepancy_type, '')
            rec.display_name = f'{product} - {dtype}'

    @api.depends('expected_qty', 'actual_qty')
    def _compute_difference(self):
        for rec in self:
            rec.difference = rec.actual_qty - rec.expected_qty

    def action_mark_reviewed(self):
        self.write({
            'state': 'reviewed',
            'reviewed_by': self.env.uid,
        })

    def action_mark_resolved(self):
        self.write({'state': 'resolved'})

    def action_ignore(self):
        self.write({'state': 'ignored'})

    @api.model
    def _cron_scan_discrepancies(self):
        """Scheduled scan: detect inventory discrepancies and missing serials."""
        _logger.info('Starting inventory discrepancy scan...')
        count = 0

        count += self._scan_serial_discrepancies()
        count += self._scan_quantity_discrepancies()

        _logger.info('Discrepancy scan complete: %d issues found', count)

    def _scan_serial_discrepancies(self):
        """Check for serial numbers in SO/invoice notes that don't exist in stock.lot."""
        count = 0
        serial_pattern = re.compile(r'(?<!\w)([\w][\w-]{3,}[\w])(?!\w)')

        recent_moves = self.env['account.move'].search([
            ('move_type', 'in', ('out_invoice', 'in_invoice')),
            ('state', '=', 'posted'),
            ('invoice_date', '>=', fields.Date.today()),
        ], limit=200)

        for move in recent_moves:
            for line in move.invoice_line_ids:
                if not line.product_id:
                    continue

                text_sources = []
                if line.name:
                    text_sources.append(line.name)

                for text in text_sources:
                    clean = re.sub(r'<[^>]+>', ' ', text)
                    candidates = serial_pattern.findall(clean)

                    for candidate in candidates:
                        if len(candidate) < 5:
                            continue
                        if candidate.lower() in ('total', 'price', 'quantity',
                                                  'subtotal', 'discount', 'amount',
                                                  'invoice', 'order', 'product'):
                            continue

                        existing = self.env['stock.lot'].search([
                            ('name', '=ilike', candidate),
                            ('product_id', '=', line.product_id.id),
                        ], limit=1)

                        if not existing:
                            already_reported = self.search([
                                ('product_id', '=', line.product_id.id),
                                ('missing_serials', 'ilike', candidate),
                                ('state', 'in', ('detected', 'reviewed')),
                            ], limit=1)

                            if not already_reported:
                                self.create({
                                    'product_id': line.product_id.id,
                                    'discrepancy_type': 'untracked_serial',
                                    'missing_serials': candidate,
                                    'source': f'Invoice {move.name}, line: {line.name[:80]}',
                                    'expected_qty': 0,
                                    'actual_qty': 0,
                                })
                                count += 1

        return count

    def _scan_quantity_discrepancies(self):
        """Compare stock.quant quantities against expected levels."""
        count = 0

        tracked_products = self.env['product.product'].search([
            ('type', '=', 'product'),
            ('tracking', '!=', 'none'),
        ], limit=500)

        for product in tracked_products:
            lots = self.env['stock.lot'].search([
                ('product_id', '=', product.id),
            ])
            quants = self.env['stock.quant'].search([
                ('product_id', '=', product.id),
                ('location_id.usage', '=', 'internal'),
            ])

            lot_ids_with_quant = set(quants.mapped('lot_id').ids)

            for lot in lots:
                if lot.id not in lot_ids_with_quant:
                    already = self.search([
                        ('product_id', '=', product.id),
                        ('discrepancy_type', '=', 'orphan_serial'),
                        ('missing_serials', '=', lot.name),
                        ('state', 'in', ('detected', 'reviewed')),
                    ], limit=1)
                    if not already:
                        self.create({
                            'product_id': product.id,
                            'discrepancy_type': 'orphan_serial',
                            'missing_serials': lot.name,
                            'source': 'Automated scan: lot exists without stock quant',
                            'expected_qty': 1,
                            'actual_qty': 0,
                        })
                        count += 1

        return count
