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

from odoo import api, fields, models

import logging

_logger = logging.getLogger(__name__)


class ResConfigSettings(models.TransientModel):
    """
    NOTES
    1. Fields with name starting with 'pos_' are removed from the vals before super call to `create`.
       Values of these fields are written to `pos_config_id` record after the super call.
       This is done so that these fields are written at the same time to the active pos.config record.
    2. During `creation` of this record, each related field is written to the source record
       *one after the other*, so constraints on the source record that are based on multiple
       fields might not work properly. However, only the *modified* related fields are written
       to the source field. But the identification of modified fields happen during the super
       call, not before `create` is called. Because of this, vals contains a lot of field before
       super call, then the number of fields is reduced after.
    """
    _inherit = 'res.config.settings'

    def _default_pos_config(self):
        # Default to the last modified pos.config.
        active_model = self.env.context.get('active_model', '')
        if active_model == 'pos.config':
            return self.env.context.get('active_id')
        return self.env['pos.config'].search([('company_id', '=', self.env.company.id)], order='write_date desc', limit=1)

    pos_config_id = fields.Many2one('pos.config', string="Point of Sale", default=lambda self: self._default_pos_config())
    sale_tax_id = fields.Many2one('account.tax', string="Default Sale Tax", related='company_id.account_sale_tax_id', readonly=False, check_company=True)
    module_pos_mercury = fields.Boolean(string="Vantiv Payment Terminal", help="The transactions are processed by Vantiv. Set your Vantiv credentials on the related payment method.")
    module_pos_adyen = fields.Boolean(string="Adyen Payment Terminal", help="The transactions are processed by Adyen. Set your Adyen credentials on the related payment method.")
    module_pos_stripe = fields.Boolean(string="Stripe Payment Terminal", help="The transactions are processed by Stripe. Set your Stripe credentials on the related payment method.")
    module_pos_six = fields.Boolean(string="Six Payment Terminal", help="The transactions are processed by Six. Set the IP address of the terminal on the related payment method.")
    module_pos_paytm = fields.Boolean(string="PayTM Payment Terminal", help="The transactions are processed by PayTM. Set your PayTM credentials on the related payment method.")
    module_pos_preparation_display = fields.Boolean(string="Preparation Display", help="Show orders on the preparation display screen.")
    update_stock_quantities = fields.Selection(related="company_id.point_of_sale_update_stock_quantities", readonly=False)
    account_default_pos_receivable_account_id = fields.Many2one(string='Default Account Receivable (PoS)', related='company_id.account_default_pos_receivable_account_id', readonly=False, check_company=True)
    barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', related='company_id.nomenclature_id', readonly=False)
    is_kiosk_mode = fields.Boolean(string="Is Kiosk Mode", default=False)

    # pos.config fields
    pos_module_pos_discount = fields.Boolean(related='pos_config_id.module_pos_discount', readonly=False)
    pos_module_pos_hr = fields.Boolean(related='pos_config_id.module_pos_hr', readonly=False)
    pos_module_pos_restaurant = fields.Boolean(related='pos_config_id.module_pos_restaurant', readonly=False)
    pos_is_order_printer = fields.Boolean(compute='_compute_pos_printer', store=True, readonly=False)
    pos_printer_ids = fields.Many2many(related='pos_config_id.printer_ids', readonly=False)

    pos_allowed_pricelist_ids = fields.Many2many('product.pricelist', compute='_compute_pos_allowed_pricelist_ids')
    pos_amount_authorized_diff = fields.Float(related='pos_config_id.amount_authorized_diff', readonly=False)
    pos_available_pricelist_ids = fields.Many2many('product.pricelist', string='Available Pricelists', compute='_compute_pos_pricelist_id', readonly=False, store=True)
    pos_cash_control = fields.Boolean(related='pos_config_id.cash_control')
    pos_cash_rounding = fields.Boolean(related='pos_config_id.cash_rounding', readonly=False, string="Cash Rounding (PoS)")
    pos_company_has_template = fields.Boolean(related='pos_config_id.company_has_template')
    pos_default_bill_ids = fields.Many2many(related='pos_config_id.default_bill_ids', readonly=False)
    pos_default_fiscal_position_id = fields.Many2one('account.fiscal.position', string='Default Fiscal Position', compute='_compute_pos_fiscal_positions', readonly=False, store=True, check_company=True)
    pos_fiscal_position_ids = fields.Many2many('account.fiscal.position', string='Fiscal Positions', compute='_compute_pos_fiscal_positions', readonly=False, store=True, check_company=True)
    pos_has_active_session = fields.Boolean(related='pos_config_id.has_active_session')
    pos_iface_available_categ_ids = fields.Many2many('pos.category', string='Available PoS Product Categories', compute='_compute_pos_iface_available_categ_ids', readonly=False, store=True)
    pos_iface_big_scrollbars = fields.Boolean(related='pos_config_id.iface_big_scrollbars', readonly=False)
    pos_iface_cashdrawer = fields.Boolean(string='Cashdrawer', compute='_compute_pos_iface_cashdrawer', readonly=False, store=True)
    pos_iface_customer_facing_display_local = fields.Boolean(related='pos_config_id.iface_customer_facing_display_local', readonly=False)
    pos_iface_customer_facing_display_via_proxy = fields.Boolean(string='Customer Facing Display', compute='_compute_pos_iface_customer_facing_display_via_proxy', readonly=False, store=True)
    pos_iface_customer_facing_display_background_image_1920 = fields.Image(related='pos_config_id.iface_customer_facing_display_background_image_1920', readonly=False)
    pos_iface_electronic_scale = fields.Boolean(string='Electronic Scale', compute='_compute_pos_iface_electronic_scale', readonly=False, store=True)
    pos_iface_print_auto = fields.Boolean(related='pos_config_id.iface_print_auto', readonly=False)
    pos_iface_print_skip_screen = fields.Boolean(related='pos_config_id.iface_print_skip_screen', readonly=False)
    pos_iface_print_via_proxy = fields.Boolean(string='Print via Proxy', compute='_compute_pos_iface_print_via_proxy', readonly=False, store=True)
    pos_iface_scan_via_proxy = fields.Boolean(string='Scan via Proxy', compute='_compute_pos_iface_scan_via_proxy', readonly=False, store=True)
    pos_iface_start_categ_id = fields.Many2one('pos.category', string='Initial Category', compute='_compute_pos_iface_start_categ_id', readonly=False, store=True)
    pos_iface_tax_included = fields.Selection(related='pos_config_id.iface_tax_included', readonly=False)
    pos_iface_tipproduct = fields.Boolean(related='pos_config_id.iface_tipproduct', readonly=False)
    pos_invoice_journal_id = fields.Many2one(related='pos_config_id.invoice_journal_id', readonly=False)
    pos_is_header_or_footer = fields.Boolean(related='pos_config_id.is_header_or_footer', readonly=False)
    pos_is_margins_costs_accessible_to_every_user = fields.Boolean(related='pos_config_id.is_margins_costs_accessible_to_every_user', readonly=False)
    pos_is_posbox = fields.Boolean(related='pos_config_id.is_posbox', readonly=False)
    pos_journal_id = fields.Many2one(related='pos_config_id.journal_id', readonly=False)
    pos_limit_categories = fields.Boolean(related='pos_config_id.limit_categories', readonly=False)
    pos_manual_discount = fields.Boolean(related='pos_config_id.manual_discount', readonly=False)
    pos_only_round_cash_method = fields.Boolean(related='pos_config_id.only_round_cash_method', readonly=False)
    pos_other_devices = fields.Boolean(related='pos_config_id.other_devices', readonly=False)
    pos_payment_method_ids = fields.Many2many(related='pos_config_id.payment_method_ids', readonly=False)
    pos_picking_policy = fields.Selection(related='pos_config_id.picking_policy', readonly=False)
    pos_picking_type_id = fields.Many2one(related='pos_config_id.picking_type_id', readonly=False)
    pos_pricelist_id = fields.Many2one('product.pricelist', string='Default Pricelist', compute='_compute_pos_pricelist_id', readonly=False, store=True)
    pos_proxy_ip = fields.Char(string='IP Address', compute='_compute_pos_proxy_ip', readonly=False, store=True)
    pos_receipt_footer = fields.Text(string='Receipt Footer', compute='_compute_pos_receipt_header_footer', readonly=False, store=True)
    pos_receipt_header = fields.Text(string='Receipt Header', compute='_compute_pos_receipt_header_footer', readonly=False, store=True)
    pos_restrict_price_control = fields.Boolean(related='pos_config_id.restrict_price_control', readonly=False)
    pos_rounding_method = fields.Many2one(related='pos_config_id.rounding_method', readonly=False)
    pos_route_id = fields.Many2one(related='pos_config_id.route_id', readonly=False)
    pos_selectable_categ_ids = fields.Many2many('pos.category', compute='_compute_pos_selectable_categ_ids')
    pos_sequence_id = fields.Many2one(related='pos_config_id.sequence_id')
    pos_set_maximum_difference = fields.Boolean(related='pos_config_id.set_maximum_difference', readonly=False)
    pos_ship_later = fields.Boolean(related='pos_config_id.ship_later', readonly=False)
    pos_start_category = fields.Boolean(related='pos_config_id.start_category', readonly=False)
    pos_tax_regime_selection = fields.Boolean(related='pos_config_id.tax_regime_selection', readonly=False)
    pos_tip_product_id = fields.Many2one('product.product', string='Tip Product', compute='_compute_pos_tip_product_id', readonly=False, store=True)
    pos_use_pricelist = fields.Boolean(related='pos_config_id.use_pricelist', readonly=False)
    pos_warehouse_id = fields.Many2one(related='pos_config_id.warehouse_id', readonly=False, string="Warehouse (PoS)")
    point_of_sale_use_ticket_qr_code = fields.Boolean(related='company_id.point_of_sale_use_ticket_qr_code', readonly=False)
    pos_auto_validate_terminal_payment = fields.Boolean(related='pos_config_id.auto_validate_terminal_payment', readonly=False, string="Automatically validates orders paid with a payment terminal.")
    pos_trusted_config_ids = fields.Many2many(related='pos_config_id.trusted_config_ids', readonly=False)
    point_of_sale_ticket_unique_code = fields.Boolean(related='company_id.point_of_sale_ticket_unique_code', readonly=False)

    @api.model_create_multi
    def create(self, vals_list):
        # STEP: Remove the 'pos' fields from each vals.
        #   They will be written atomically to `pos_config_id` after the super call.
        pos_config_id_to_fields_vals_map = {}

        for vals in vals_list:
            pos_config_id = vals.get('pos_config_id')
            if pos_config_id:
                pos_fields_vals = {}

                if vals.get('pos_cash_rounding'):
                    vals['group_cash_rounding'] = True

                if vals.get('pos_use_pricelist'):
                    vals['group_product_pricelist'] = True

                for field in self._fields.values():
                    if field.name == 'pos_config_id':
                        continue

                    val = vals.get(field.name)

                    # Add only to pos_fields_vals if
                    #   1. _field is in vals -- meaning, the _field is in view.
                    #   2. _field starts with 'pos_' -- meaning, the _field is a pos field.
                    if field.name.startswith('pos_') and val is not None:
                        pos_config_field_name = field.name[4:]
                        if not pos_config_field_name in self.env['pos.config']._fields:
                            _logger.warning("The value of '%s' is not properly saved to the pos_config_id field because the destination"
                                " field '%s' is not a valid field in the pos.config model.", field.name, pos_config_field_name)
                        else:
                            pos_fields_vals[pos_config_field_name] = val
                            del vals[field.name]

                pos_config_id_to_fields_vals_map[pos_config_id] = pos_fields_vals

        # STEP: Call super on the modified vals_list.
        # NOTE: When creating `res.config.settings` records, it doesn't write on *unmodified* related fields.
        result = super().create(vals_list)

        # STEP: Finally, we write the value of 'pos' fields to 'pos_config_id'.
        for pos_config_id, pos_fields_vals in pos_config_id_to_fields_vals_map.items():
            pos_config = self.env['pos.config'].browse(pos_config_id)
            pos_config.with_context(from_settings_view=True).write(pos_fields_vals)

        return result

    def set_values(self):
        super(ResConfigSettings, self).set_values()
        if not self.group_product_pricelist:
            self.env['pos.config'].search([
                ('use_pricelist', '=', True)
            ]).use_pricelist = False

        if not self.group_cash_rounding:
            self.env['pos.config'].search([
                ('cash_rounding', '=', True)
            ]).cash_rounding = False

    def action_pos_config_create_new(self):
        return {
            'view_mode': 'form',
            'res_model': 'pos.config',
            'type': 'ir.actions.act_window',
            'target': 'new',
            'res_id': False,
            'context': {'pos_config_open_modal': True, 'pos_config_create_mode': True},
        }

    def pos_open_ui(self):
        if self._context.get('pos_config_id'):
            pos_config_id = self._context['pos_config_id']
            pos_config = self.env['pos.config'].browse(pos_config_id)
            return pos_config.open_ui()

    @api.model
    def _is_cashdrawer_displayed(self, res_config):
        return res_config.pos_iface_print_via_proxy

    @api.depends('pos_module_pos_restaurant', 'pos_config_id')
    def _compute_pos_printer(self):
        for res_config in self:
            res_config.update({
                'pos_is_order_printer': res_config.pos_config_id.is_order_printer,
            })

    @api.depends('pos_limit_categories', 'pos_config_id')
    def _compute_pos_iface_available_categ_ids(self):
        for res_config in self:
            if not res_config.pos_limit_categories:
                res_config.pos_iface_available_categ_ids = False
            else:
                res_config.pos_iface_available_categ_ids = res_config.pos_config_id.iface_available_categ_ids

    @api.depends('pos_start_category', 'pos_config_id')
    def _compute_pos_iface_start_categ_id(self):
        for res_config in self:
            if not res_config.pos_start_category:
                res_config.pos_iface_start_categ_id = False
            else:
                res_config.pos_iface_start_categ_id = res_config.pos_config_id.iface_start_categ_id

    @api.depends('pos_iface_available_categ_ids')
    def _compute_pos_selectable_categ_ids(self):
        for res_config in self:
            if res_config.pos_iface_available_categ_ids:
                res_config.pos_selectable_categ_ids = res_config.pos_iface_available_categ_ids
            else:
                res_config.pos_selectable_categ_ids = self.env['pos.category'].search([])

    @api.depends('pos_iface_print_via_proxy', 'pos_config_id')
    def _compute_pos_iface_cashdrawer(self):
        for res_config in self:
            if self._is_cashdrawer_displayed(res_config):
                res_config.pos_iface_cashdrawer = res_config.pos_config_id.iface_cashdrawer
            else:
                res_config.pos_iface_cashdrawer = False

    @api.depends('pos_is_header_or_footer', 'pos_config_id')
    def _compute_pos_receipt_header_footer(self):
        for res_config in self:
            if res_config.pos_is_header_or_footer:
                res_config.pos_receipt_header = res_config.pos_config_id.receipt_header
                res_config.pos_receipt_footer = res_config.pos_config_id.receipt_footer
            else:
                res_config.pos_receipt_header = False
                res_config.pos_receipt_footer = False

    @api.depends('pos_tax_regime_selection', 'pos_config_id')
    def _compute_pos_fiscal_positions(self):
        for res_config in self:
            if res_config.pos_tax_regime_selection:
                res_config.pos_default_fiscal_position_id = res_config.pos_config_id.default_fiscal_position_id
                res_config.pos_fiscal_position_ids = res_config.pos_config_id.fiscal_position_ids
            else:
                res_config.pos_default_fiscal_position_id = False
                res_config.pos_fiscal_position_ids = [(5, 0, 0)]

    @api.depends('pos_iface_tipproduct', 'pos_config_id')
    def _compute_pos_tip_product_id(self):
        for res_config in self:
            if res_config.pos_iface_tipproduct:
                res_config.pos_tip_product_id = res_config.pos_config_id.tip_product_id
            else:
                res_config.pos_tip_product_id = False

    @api.depends('pos_use_pricelist', 'pos_config_id', 'pos_journal_id')
    def _compute_pos_pricelist_id(self):
        for res_config in self:
            currency_id = res_config.pos_journal_id.currency_id.id if res_config.pos_journal_id.currency_id else res_config.pos_config_id.company_id.currency_id.id
            pricelists_in_current_currency = self.env['product.pricelist'].search([
                *self.env['product.pricelist']._check_company_domain(res_config.pos_config_id.company_id),
                ('currency_id', '=', currency_id),
            ])
            if not res_config.pos_use_pricelist:
                res_config.pos_pricelist_id = False
                res_config.pos_available_pricelist_ids = res_config.pos_config_id.available_pricelist_ids
            else:
                if any([p.currency_id.id != currency_id for p in res_config.pos_available_pricelist_ids]):
                    res_config.pos_available_pricelist_ids = pricelists_in_current_currency
                    res_config.pos_pricelist_id = pricelists_in_current_currency[:1]
                else:
                    res_config.pos_available_pricelist_ids = res_config.pos_config_id.available_pricelist_ids
                    res_config.pos_pricelist_id = res_config.pos_config_id.pricelist_id

    @api.depends('pos_available_pricelist_ids', 'pos_use_pricelist')
    def _compute_pos_allowed_pricelist_ids(self):
        for res_config in self:
            if res_config.pos_use_pricelist:
                res_config.pos_allowed_pricelist_ids = res_config.pos_available_pricelist_ids.ids
            else:
                res_config.pos_allowed_pricelist_ids = self.env['product.pricelist'].search([]).ids

    @api.depends('pos_is_posbox', 'pos_config_id')
    def _compute_pos_proxy_ip(self):
        for res_config in self:
            if not res_config.pos_is_posbox:
                res_config.pos_proxy_ip = False
            else:
                res_config.pos_proxy_ip = res_config.pos_config_id.proxy_ip

    @api.depends('pos_is_posbox', 'pos_config_id')
    def _compute_pos_iface_print_via_proxy(self):
        for res_config in self:
            if not res_config.pos_is_posbox:
                res_config.pos_iface_print_via_proxy = False
            else:
                res_config.pos_iface_print_via_proxy = res_config.pos_config_id.iface_print_via_proxy

    @api.depends('pos_is_posbox', 'pos_config_id')
    def _compute_pos_iface_scan_via_proxy(self):
        for res_config in self:
            if not res_config.pos_is_posbox:
                res_config.pos_iface_scan_via_proxy = False
            else:
                res_config.pos_iface_scan_via_proxy = res_config.pos_config_id.iface_scan_via_proxy

    @api.depends('pos_is_posbox', 'pos_config_id')
    def _compute_pos_iface_electronic_scale(self):
        for res_config in self:
            if not res_config.pos_is_posbox:
                res_config.pos_iface_electronic_scale = False
            else:
                res_config.pos_iface_electronic_scale = res_config.pos_config_id.iface_electronic_scale

    @api.depends('pos_is_posbox', 'pos_config_id')
    def _compute_pos_iface_customer_facing_display_via_proxy(self):
        for res_config in self:
            if not res_config.pos_is_posbox:
                res_config.pos_iface_customer_facing_display_via_proxy = False
            else:
                res_config.pos_iface_customer_facing_display_via_proxy = res_config.pos_config_id.iface_customer_facing_display_via_proxy

    @api.onchange('pos_trusted_config_ids')
    def _onchange_trusted_config_ids(self):
        for config in self:
            removed_trusted_configs = set(config.pos_config_id.trusted_config_ids.ids) - set(config.pos_trusted_config_ids.ids)
            for old in config.pos_config_id.trusted_config_ids:
                if config.pos_config_id.id not in old.trusted_config_ids.ids:
                    old._add_trusted_config_id(config.pos_config_id)
                if old.id in removed_trusted_configs:
                    old._remove_trusted_config_id(config.pos_config_id)
