import uuid

from odoo.tools import _
from odoo.exceptions import ValidationError

DEMO_PRIVATE_KEY = 'l10n_dk_nemhandel/tools/private_key.pem'

# -------------------------------------------------------------------------
# HELPERS
# -------------------------------------------------------------------------


def _get_notification_message():
    title = _("Registered to receive documents via Nemhandel (demo).")
    message = _("You can now fake sending invoices in demo mode.")
    return title, message

# -------------------------------------------------------------------------
# MOCKED FUNCTIONS
# -------------------------------------------------------------------------


def _mock_call_nemhandel_proxy(func, self, *args, **kwargs):

    def _mock_get_all_documents(user, args, kwargs):
        return {'messages': []}

    def _mock_get_document(user, args, kwargs):
        message_uuid = args[1]['message_uuids'][0]
        return {message_uuid: {
            'state': 'done',
            'origin_message_uuid': message_uuid,
            'document_type': 'Invoice'
        }}

    def _mock_send_document(user, args, kwargs):
        # Trigger the reception of vendor bills
        get_messages_cron = user.env['ir.cron'].sudo().env.ref(
            'l10n_dk_nemhandel.ir_cron_nemhandel_get_new_documents',
            raise_if_not_found=False,
        )
        if get_messages_cron:
            get_messages_cron._trigger()
        return {
            'messages': [{
                'message_uuid': 'demo_%s' % uuid.uuid4(),
            } for i in args[1]['documents']],
        }

    endpoint = args[0].split('/')[-1]
    return {
        'ack': lambda _user, _args, _kwargs: {},
        'activate_participant': lambda _user, _args, _kwargs: {},
        'get_all_documents': _mock_get_all_documents,
        'get_document': _mock_get_document,
        'participant_status': lambda _user, _args, _kwargs: {'nemhandel_state': 'active'},
        'send_document': _mock_send_document,
        'set_webhook': lambda _user, _args, _kwargs: {}
    }[endpoint](self, args, kwargs)


def _mock_button_verify_partner_endpoint(func, self, *args, **kwargs):
    self.ensure_one()
    if self.nemhandel_identifier_type and self.nemhandel_identifier_value:
        self.nemhandel_verification_state = 'valid'


def _mock_user_creation(func, self, *args, **kwargs):
    func(self, *args, **kwargs)
    self.l10n_dk_nemhandel_proxy_state = 'receiver'
    return self._action_send_notification(
        *_get_notification_message(),
    )


def _mock_receiver_registration(func, self, *args, **kwargs):
    if not self.phone_number:
        raise ValidationError(_("Please enter a phone number to verify your application."))
    if not self.contact_email:
        raise ValidationError(_("Please enter a primary contact email to verify your application."))
    self.edi_user_id = self.edi_user_id.sudo()._register_proxy_user(self.company_id, 'nemhandel', self.edi_mode)
    self.l10n_dk_nemhandel_proxy_state = 'receiver'
    return self.env['nemhandel.registration']._action_send_notification(
        *_get_notification_message(),
    )


def _mock_check_verification_code(func, self, *args, **kwargs):
    self.button_nemhandel_registration_sms()
    self.edi_user_id.nemhandel_verification_code = False


def _mock_deregister_participant(func, self, *args, **kwargs):
    # Set documents sent in demo to a state where they can be re-sent
    demo_moves = self.env['account.move'].search([
        ('company_id', '=', self.company_id.id),
        ('nemhandel_message_uuid', '=like', 'demo_%'),
    ])
    demo_moves.write({
        'nemhandel_message_uuid': None,
        'nemhandel_move_state': None,
    })
    demo_moves.message_main_attachment_id.unlink()
    demo_moves.ubl_cii_xml_id.unlink()
    log_message = _('The Nemhandel status of the documents has been reset when switching from Demo to Live.')
    demo_moves._message_log_batch(bodies={move.id: log_message for move in demo_moves})

    # also unlink the demo vendor bill
    self.env['account.move'].search([
        ('company_id', '=', self.company_id.id),
        ('nemhandel_message_uuid', '=', f'{self.company_id.id}_demo_vendor_bill'),
    ]).unlink()

    if 'nemhandel_edi_user' in self._fields:
        self.nemhandel_edi_user.unlink()
    else:
        self.edi_user_id.unlink()
    self.l10n_dk_nemhandel_proxy_state = 'not_registered'
    if 'nemhandel_edi_mode' in self._fields:
        self.nemhandel_edi_mode = 'demo'


def _mock_update_user_data(func, self, *args, **kwargs):
    pass


def _mock_check_user_on_alternative_service(func, self, *args, **kwargs):
    pass


def _mock_get_nemhandel_verification_state(func, self, *args, **kwargs):
    xml_format = args
    if not (self.nemhandel_identifier_type and self.nemhandel_identifier_value):
        return 'not_verified'
    if not xml_format:
        return 'not_valid'
    return 'valid'


_demo_behaviour = {
    'button_nemhandel_check_partner_endpoint': _mock_button_verify_partner_endpoint,
    'button_nemhandel_registration_sms': _mock_receiver_registration,
    'button_deregister_nemhandel_participant': _mock_deregister_participant,
    'button_update_nemhandel_user_data': _mock_update_user_data,
    'button_check_nemhandel_verification_code': _mock_check_verification_code,
    '_check_user_on_alternative_service': _mock_check_user_on_alternative_service,
    '_get_nemhandel_verification_state': _mock_get_nemhandel_verification_state,
    '_call_nemhandel_proxy': _mock_call_nemhandel_proxy,
}

# -------------------------------------------------------------------------
# DECORATORS
# -------------------------------------------------------------------------


def handle_demo(func, /):
    """ This decorator is used on methods that should be mocked in demo mode.

    Handle the decision: "Are we in demo mode?", and conditionally decide which function to execute.
    """
    def wrapped(self, *args, **kwargs):
        if self.env.company._get_nemhandel_edi_mode() == 'demo':
            return _demo_behaviour[func.__name__](func, self, *args, **kwargs)
        return func(self, *args, **kwargs)
    return wrapped
