# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, models, _
from odoo.tools import format_datetime
from markupsafe import Markup


rec = 0
def autoIncrement():
    global rec
    pStart = 1
    pInterval = 1
    if rec == 0:
        rec = pStart
    else:
        rec += pInterval
    return rec


class MrpStockReport(models.TransientModel):
    _name = 'stock.traceability.report'
    _description = 'Traceability Report'

    @api.model
    def _get_move_lines(self, move_lines, line_id=None):
        lines_seen = move_lines
        lines_todo = list(move_lines)
        while lines_todo:
            move_line = lines_todo.pop(0)
            # if MTO
            if move_line.move_id.move_orig_ids:
                lines = move_line.move_id.move_orig_ids.mapped('move_line_ids').filtered(
                    lambda m: m.lot_id == move_line.lot_id and m.state == 'done'
                ) - lines_seen
            # if MTS
            elif move_line.location_id.usage in ('internal', 'transit'):
                lines = self.env['stock.move.line'].search([
                    ('product_id', '=', move_line.product_id.id),
                    ('lot_id', '=', move_line.lot_id.id),
                    ('location_dest_id', '=', move_line.location_id.id),
                    ('id', 'not in', lines_seen.ids),
                    ('date', '<=', move_line.date),
                    ('state', '=', 'done')
                ])
            else:
                continue
            if line_id is None or line_id in lines.ids:
                lines_todo += list(lines)
            lines_seen |= lines
        return lines_seen - move_lines

    @api.model
    def get_lines(self, line_id=False, **kw):
        context = dict(self.env.context)
        model = kw and kw['model_name'] or context.get('model')
        rec_id = kw and kw['model_id'] or context.get('active_id')
        level = kw and kw['level'] or 1
        lines = self.env['stock.move.line']
        move_line = self.env['stock.move.line']
        if rec_id and model == 'stock.lot':
            lines = move_line.search([
                ('lot_id', '=', context.get('lot_name') or rec_id),
                ('state', '=', 'done'),
            ])
        elif  rec_id and model == 'stock.move.line' and context.get('lot_name'):
            record = self.env[model].browse(rec_id)
            dummy, is_used = self._get_linked_move_lines(record)
            if is_used:
                lines = is_used
        elif rec_id and model in ('stock.picking', 'mrp.production'):
            record = self.env[model].browse(rec_id)
            if model == 'stock.picking':
                lines = record.move_ids.move_line_ids.filtered(lambda m: m.lot_id and m.state == 'done')
            else:
                lines = record.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.state == 'done')
        move_line_vals = self._lines(line_id, model_id=rec_id, model=model, level=level, move_lines=lines)
        final_vals = sorted(move_line_vals, key=lambda v: v['date'], reverse=True)
        lines = self._final_vals_to_lines(final_vals, level)
        return lines

    @api.model
    def _get_reference(self, move_line):
        res_model = ''
        ref = ''
        res_id = False
        picking_id = move_line.picking_id or move_line.move_id.picking_id
        if picking_id:
            res_model = 'stock.picking'
            res_id = picking_id.id
            ref = picking_id.name
        elif move_line.move_id.is_inventory:
            res_model = 'stock.move'
            res_id = move_line.move_id.id
            ref = 'Inventory Adjustment'
        elif move_line.move_id.scrapped and move_line.move_id.scrap_id:
            res_model = 'stock.scrap'
            res_id = move_line.move_id.scrap_id.id
            ref = move_line.move_id.scrap_id.name
        return res_model, res_id, ref

    @api.model
    def _quantity_to_str(self, from_uom, to_uom, qty):
        """ workaround to apply the float rounding logic of t-esc on data prepared server side """
        qty = from_uom._compute_quantity(qty, to_uom, rounding_method='HALF-UP')
        return self.env['ir.qweb.field.float'].value_to_html(qty, {'decimal_precision': 'Product Unit of Measure'})

    def _get_usage(self, move_line):
        usage = ''
        if (move_line.location_id.usage == 'internal') and (move_line.location_dest_id.usage == 'internal'):
            usage = 'internal'
        elif (move_line.location_id.usage != 'internal') and (move_line.location_dest_id.usage == 'internal'):
            usage = 'in'
        else:
            usage = 'out'
        return usage

    def _make_dict_move(self, level, parent_id, move_line, unfoldable=False):
        res_model, res_id, ref = self._get_reference(move_line)
        dummy, is_used = self._get_linked_move_lines(move_line)
        data = [{
            'level': level,
            'unfoldable': unfoldable,
            'date': move_line.move_id.date,
            'parent_id': parent_id,
            'is_used': bool(is_used),
            'usage': self._get_usage(move_line),
            'model_id': move_line.id,
            'model': 'stock.move.line',
            'product_id': move_line.product_id.display_name,
            'product_qty_uom': "%s %s" % (self._quantity_to_str(move_line.product_uom_id, move_line.product_id.uom_id, move_line.quantity), move_line.product_id.uom_id.name),
            'lot_name': move_line.lot_id.name,
            'lot_id': move_line.lot_id.id,
            'location_source': move_line.location_id.name,
            'location_destination': move_line.location_dest_id.name,
            'reference_id': ref,
            'res_id': res_id,
            'res_model': res_model}]
        return data

    @api.model
    def _final_vals_to_lines(self, final_vals, level):
        lines = []
        for data in final_vals:
            lines.append({
                'id': autoIncrement(),
                'model': data['model'],
                'model_id': data['model_id'],
                'parent_id': data['parent_id'],
                'usage': data.get('usage', False),
                'is_used': data.get('is_used', False),
                'lot_name': data.get('lot_name', False),
                'lot_id': data.get('lot_id', False),
                'reference': data.get('reference_id', False),
                'res_id': data.get('res_id', False),
                'res_model': data.get('res_model', False),
                'columns': [data.get('reference_id', False),
                            data.get('product_id', False),
                            format_datetime(self.env, data.get('date', False), tz=False, dt_format=False),
                            data.get('lot_name', False),
                            data.get('location_source', False),
                            data.get('location_destination', False),
                            data.get('product_qty_uom', 0)],
                'level': level,
                'unfoldable': data['unfoldable'],
            })
        return lines

    def _get_linked_move_lines(self, move_line):
        """ This method will return the consumed line or produced line for this operation."""
        return False, False

    @api.model
    def _lines(self, line_id=False, model_id=False, model=False, level=0, move_lines=None, **kw):
        final_vals = []
        lines = move_lines or []
        if model and line_id:
            move_line = self.env[model].browse(model_id)
            move_lines, is_used = self._get_linked_move_lines(move_line)
            if move_lines:
                lines = move_lines
            else:
                # Traceability in case of consumed in.
                lines = self._get_move_lines(move_line, line_id=line_id)
        for line in lines:
            unfoldable = False
            if line.consume_line_ids or (model != "stock.lot" and line.lot_id and self._get_move_lines(line)):
                unfoldable = True
            final_vals += self._make_dict_move(level, parent_id=line_id, move_line=line, unfoldable=unfoldable)
        return final_vals

    def get_pdf_lines(self, line_data=[]):
        lines = []
        for line in line_data:
            model = self.env[line['model_name']].browse(line['model_id'])
            unfoldable = False
            if line.get('unfoldable'):
                unfoldable = True
            final_vals = self._make_dict_move(line['level'], parent_id=line['id'], move_line=model, unfoldable=unfoldable)
            lines.append(self._final_vals_to_lines(final_vals, line['level'])[0])
        return lines

    def get_pdf(self, line_data=None):
        line_data = [] if line_data is None else line_data
        lines = self.with_context(print_mode=True).get_pdf_lines(line_data)
        base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
        rcontext = {
            'mode': 'print',
            'base_url': base_url,
        }

        context = dict(self.env.context)
        if context.get('active_id') and context.get('active_model'):
            rcontext['reference'] = self.env[context.get('active_model')].browse(int(context.get('active_id'))).display_name

        body = self.env['ir.ui.view'].with_context(context)._render_template(
            "stock.report_stock_inventory_print",
            values=dict(rcontext, lines=lines, report=self, context=self),
        )

        header = self.env['ir.actions.report']._render_template("web.internal_layout", values=rcontext)
        header = self.env['ir.actions.report']._render_template("web.minimal_layout", values=dict(rcontext, subst=True, body=Markup(header.decode())))

        return self.env['ir.actions.report']._run_wkhtmltopdf(
            [body],
            header=header.decode(),
            landscape=True,
            specific_paperformat_args={'data-report-margin-top': 30, 'data-report-header-spacing': 25}
        )

    def _get_main_lines(self):
        context = dict(self.env.context)
        return self.with_context(context).get_lines()

    @api.model
    def get_main_lines(self, given_context=None):
        res = self.search([('create_uid', '=', self.env.uid)], limit=1)
        if not res:
            return self.create({}).with_context(given_context)._get_main_lines()
        return res.with_context(given_context)._get_main_lines()
