# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.

import requests
from datetime import datetime as dt
from base64 import b64encode
import json

from odoo import api, fields, models


class SignifydConnector(models.Model):
    _name = 'signifyd.connector'
    _description = 'Interact with Signifyd API'

    name = fields.Char(string='Connector Name', required=True)
    test_mode = fields.Boolean(string='Test Mode')
    secret_key = fields.Char(string='API Key', required=True)
    secret_key_test = fields.Char(string='TEST API Key')
    webhooks_registered = fields.Boolean(string='Successfully Registered Webhooks')
    notify_user_ids = fields.Many2many('res.users', string='Receive decline notifications')
    website_ids = fields.One2many('website', 'signifyd_connector_id', string='Used on Websites')
    signifyd_case_type = fields.Selection([
        ('', 'No Case'),
        ('SCORE', 'Score'),
        ('DECISION', 'Decision'),
        ('GUARANTEE', 'Guarantee'),
    ], string='Default Case Creation', help='Used for internal/admin orders, overridden by payment acquirer.',
        required=True, default='')

    # TODO ideally this would be a regular constant
    # however other entities currently use this by reference
    API_URL = 'https://api.signifyd.com/v2'

    def get_headers(self):
        self.ensure_one()
        # Check for prod or test mode
        if self.test_mode:
            api_key = self.secret_key_test
        else:
            api_key = self.secret_key

        b64_auth_key = b64encode(api_key.encode()).decode().replace('=', '')

        headers = {
            'Authorization': 'Basic ' + b64_auth_key,
            'Content-Type': 'application/json',
        }

        return headers

    def register_webhooks(self):
        self.ensure_one()
        headers = self.get_headers()
        # This should come from the website...
        # we may need a better way to link the connector to the website.
        base_url = None
        website = self.env['website'].search([('signifyd_connector_id', '=', self.id)], limit=1)
        if website and website.domain:
            base_url = website.domain
            if base_url.find('://') <= 0:  # documentation says if no protocol use http
                base_url = 'http://' + base_url
        if not base_url:
            base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
        values = {
            "webhooks": [
                # Given we are creating the cases, we do not need to know about it
                # {
                #     "event": "CASE_CREATION",
                #     "url": base_url + "/signifyd/cases/update"
                # },
                {
                    "event": "CASE_RESCORE",
                    "url": base_url + "/signifyd/cases/update"
                },
                {
                    "event": "CASE_REVIEW",
                    "url": base_url + "/signifyd/cases/update"
                },
                {
                    "event": "GUARANTEE_COMPLETION",
                    "url": base_url + "/signifyd/cases/update"
                },
                {
                    "event": "DECISION_MADE",
                    "url": base_url + "/signifyd/cases/update"
                },
            ]
        }
        data = json.dumps(values, indent=4)
        r = requests.post(
            self.API_URL + '/teams/webhooks',
            headers=headers,
            data=data,
        )
        # r.raise_for_status()
        return r

    def action_register_webhooks(self):
        notification = {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': ('Signifyd Connector'),
                'sticky': True,
            },
        }

        res = self.register_webhooks()

        if 200 <= res.status_code < 300:
            notification['params']['type'] = 'success'
            notification['params']['message'] = 'Successfully registered webhooks with Signifyd.'
            self.webhooks_registered = True
            return notification

        else:
            notification['params']['type'] = 'danger'
            notification['params']['message'] = res.content.decode('utf-8')
            try:
                # trying to make a better error, not be exhaustive with error handling.
                object = json.loads(notification['params']['message'])
                notification['params']['message'] = '\n'.join([e[0] for e in (object.get('errors') or {}).values()])
            except:
                pass
            self.webhooks_registered = False
            return notification

    def process_post_values(self, post):
        # Construct dict from request data for endpoints
        uuid = post.get('uuid')
        case_id = post.get('caseId')
        team_name = post.get('teamName')
        team_id = post.get('teamId')
        review_disposition = post.get('reviewDisposition')
        guarantee_disposition = post.get('guaranteeDisposition')
        order_outcome = post.get('orderOutcome')
        status = post.get('status')
        score = post.get('score')
        disposition_reason = post.get('dispositionReason')
        disposition = post.get('disposition')
        checkpoint_action = post.get('checkpointAction', '')
        if not checkpoint_action and guarantee_disposition:
            if guarantee_disposition == 'APPROVED':
                checkpoint_action = 'ACCEPT'
            elif guarantee_disposition == 'DECLINED':
                checkpoint_action = 'REJECT'
            else:
                checkpoint_action = 'HOLD'
        last_update = str(dt.now())

        values = {}

        # Validate that the order and case match the request
        values.update({'uuid': uuid}) if uuid else ''
        values.update({'team_name': team_name}) if team_name else ''
        values.update({'team_id': team_id}) if team_id else ''
        values.update({'review_disposition': review_disposition}) if review_disposition else ''
        values.update({'guarantee_disposition': guarantee_disposition}) if guarantee_disposition else ''
        values.update({'order_outcome': order_outcome}) if order_outcome else ''
        values.update({'status': status}) if status else ''
        values.update({'score': score}) if score else ''
        values.update({'case_id': case_id}) if case_id else ''
        values.update({'disposition_reason': disposition_reason}) if disposition_reason else ''
        values.update({'disposition': disposition}) if disposition else ''
        values.update({'last_update': last_update})
        values.update({'checkpoint_action': checkpoint_action})

        return values
