# -*- coding: utf-8 -*-

from odoo import models, fields, api, exceptions
from datetime import timedelta, datetime
from collections import defaultdict
import logging

_logger = logging.getLogger(__name__)


class MklabForecast(models.Model):
    _name = 'mklab.forecast'

    name = fields.Char(string='Название', compute='_compute_name')
    start_date = fields.Date(string='Дата прогноза', required=True)
    forecast_line_ids = fields.One2many(
        comodel_name='mklab.forecastline',
        inverse_name='forecast_id',
        string='Строки',
    )
    partner_ids = fields.Many2many(
        comodel_name='res.partner',
        string='Ограничить контрагентами',
    )
    expected_growth = fields.Float(
        string='Ожидаемый рост продаж при акции, %',
        required=True,
        default=20,
    )
    is_promo = fields.Boolean(string='Запланирована акция', compute='_compute_is_promo')
    is_ready_to_calc = fields.Boolean(string='Отправить на расчет')
    total_amount = fields.Float(string='Итого', compute='_compute_total_amount')
    state = fields.Selection(
        selection=[('draft', 'Черновик'), ('active', 'Утвержден')],
        string='Статус',
        default='draft',
    )

    def _compute_name(self):
        for s in self:
            s.name = str(s.start_date) if s.start_date else ''

    def _compute_total_amount(self):
        for s in self:
            s.total_amount = sum(s.forecast_line_ids.mapped(lambda r: r.total_amount))

    def _compute_is_promo(self):
        for s in self:
            promos = self.env['mklab.marketaction'].search([
                ('start_date', '<=', s.start_date),
                ('end_date', '>=', s.start_date),
            ])
            s.is_promo = bool(promos)

    @api.constrains('is_ready_to_calc')
    def _reset_calculated(self):
        for s in self:
            s.forecast_line_ids.write({'is_calculated': False})

    def action_validate(self):
        self.state = 'active'

    def action_cancel(self):
        self.state = 'draft'

    def fill_lines(self):
        for s in self:
            if s.forecast_line_ids:
                raise exceptions.UserError(
                    'Для автоматического заполнения нужно сначала очистить все строки')
            line_obj = self.env['mklab.forecastline']
            products = self.env['product.product'].search([('is_forecast_ok', '=', True)])
            for prod in products:
                line_obj.create({
                    'forecast_id': s.id,
                    'product_id': prod.id,
                    'price': prod.list_price,
                })

    def create_mrp(self):
        for s in self:
            lines = s.forecast_line_ids.filtered(
                lambda r: r.forecast_value > 0 and not r.mrp_order_id)
            for line in lines:
                bom = line.bom_id
                if not bom:
                    raise exceptions.UserError(
                        'Для продукта ' + line.product_id.name + ' не найдена ведомость материалов')
                new_order = self.env['mrp.production'].create({
                    'product_id': line.product_id.id,
                    'product_qty': line.forecast_value,
                    'bom_id': bom.id,
                    'product_uom_id': line.product_id.uom_id.id,
                    'origin': s.name,
                    'company_id': self.env.user.company_id.id,
                    'date_start': s.start_date,
                })
                line.mrp_order_id = new_order


class MklabForecastLine(models.Model):
    _name = 'mklab.forecastline'

    product_id = fields.Many2one(
        comodel_name='product.product',
        string='Товар',
        required=True,
    )
    forecast_value = fields.Float(string='Прогноз/план')
    order_value = fields.Float(string='В заказах факт')
    invoice_value = fields.Float(string='Отгружено факт')
    forecast_id = fields.Many2one(
        comodel_name='mklab.forecast',
        string='Прогноз',
    )
    is_calculated = fields.Boolean(string='Пересчитано автоматом')
    price = fields.Float(string='Цена')
    total_amount = fields.Float(string='Итого', compute='_compute_total_amount')
    mrp_order_id = fields.Many2one(
        comodel_name='mrp.production',
        string='Заказ на производство',
    )
    notes = fields.Text(string='Комментарий')
    bom_id = fields.Many2one(
        comodel_name='mrp.bom',
        string='Ведомость материалов',
    )

    def _compute_total_amount(self):
        for s in self:
            s.total_amount = s.price * s.forecast_value

    def calc_bom(self):
        for s in self:
            s.bom_id, s.notes = s.product_id.start_planning(s.forecast_value)

    def get_fact_values(self):
        for s in self:
            for_start = datetime.combine(s.forecast_id.start_date, datetime.min.time())
            for_end = for_start + timedelta(hours=23)

            if s.forecast_id.partner_ids:
                orders = self.env['sale.order'].search([
                    ('date_order', '<=', for_end),
                    ('date_order', '>=', for_start),
                    ('partner_id', 'in', s.forecast_id.partner_ids.ids),
                ])
            else:
                orders = self.env['sale.order'].search([
                    ('date_order', '<=', for_end),
                    ('date_order', '>=', for_start),
                ])
            ord_value = 0
            inv_value = 0
            for order in orders:
                for line in order.order_line.filtered(lambda r: r.product_id == s.product_id):
                    ord_value += line.product_uom_qty
                for inv in order.invoice_ids:
                    for line in inv.invoice_line_ids.filtered(lambda r: r.product_id == s.product_id):
                        inv_value += line.quantity
            s.invoice_value = inv_value
            s.order_value = ord_value

    def get_forecast_values(self):
        for s in self:
            start_date_forforecast = s.forecast_id.start_date - timedelta(days=84)
            weekday = s.forecast_id.start_date.weekday()            

            if s.forecast_id.partner_ids:
                orders = self.env['sale.order'].search([
                    ('date_order', '<', s.forecast_id.start_date),
                    ('date_order', '>=', start_date_forforecast),
                    ('dayofweek', '=', weekday),
                    ('partner_id', 'in', s.forecast_id.partner_ids.ids),
                ])
            else:
                orders = self.env['sale.order'].search([
                    ('date_order', '<', s.forecast_id.start_date),
                    ('date_order', '>=', start_date_forforecast),
                    ('dayofweek', '=', weekday),
                ])           
            f_value = 0
            promos_forecast = self.env['mklab.marketaction'].search([
                ('start_date', '<=', s.forecast_id.start_date),
                ('end_date', '>=', s.forecast_id.start_date),
            ])
            for order in orders:
                promos = self.env['mklab.marketaction'].search([
                    ('start_date', '<=', order.date_order),
                    ('end_date', '>=', order.date_order),
                ])
                koeff = 0
                if promos:
                    mark_lines = self.env['mklab.productline'].search([
                        ('action_id', '=', promos[0].id),
                        ('product_id', '=', s.product_id.id),
                    ])
                    pids = self.env['mklab.partnerline'].search([
                        ('action_id', '=', promos[0].id),
                        ('partner_id', '=', order.partner_id.id),
                    ])
                    if mark_lines and (pids or not promos[0].partner_line_ids):
                        koeff = mark_lines[0].expected_growth

                is_promo = False
                if promos_forecast:
                    mark_lines_f = self.env['mklab.productline'].search([
                        ('action_id', '=', promos_forecast[0].id),
                        ('product_id', '=', s.product_id.id),
                    ])
                    pids_f = self.env['mklab.partnerline'].search([
                        ('action_id', '=', promos_forecast[0].id),
                        ('partner_id', '=', order.partner_id.id),
                    ])
                    if mark_lines_f and (pids_f or not promos_forecast[0].partner_line_ids):
                        is_promo = True

                order_qty = 0
                for line in order.order_line.filtered(lambda r: r.product_id == s.product_id):
                    line_qty = (line.product_uom_qty * (1 + koeff / 100) * (1 + s.forecast_id.expected_growth / 100)
                                if is_promo else line.product_uom_qty * (1 + koeff / 100))
                    f_value += line_qty
                    order_qty += line_qty                
            s.forecast_value = round(f_value / 12, 0)

    @api.model
    def recalc_all(self):
        """Cron: пересчитывает по 5 строк за раз."""
        lines = self.env['mklab.forecastline'].search(
            [('forecast_id.is_ready_to_calc', '=', True), ('is_calculated', '=', False)],
            limit=5,
        )
        for line in lines:
            line.get_forecast_values()
            line.get_fact_values()
            line.is_calculated = True
