# coding: utf-8

"""
    Fingerprint Server API

    Fingerprint Server API allows you to get information about visitors and about individual events in a server environment. This API can be used for data exports, decision-making, and data analysis scenarios.  # noqa: E501

    OpenAPI spec version: 3
    Contact: support@fingerprint.com
    Generated by: https://github.com/swagger-api/swagger-codegen.git
"""

import io
import os
import unittest

import urllib3

from fingerprint_pro_server_api_sdk import (Configuration, ErrorResponse, ErrorPlainResponse, ErrorCode,
                                            RawDeviceAttributes, EventsUpdateRequest, RelatedVisitorsResponse,
                                            SearchEventsResponse, SearchEventsResponseEvents, Products)
from fingerprint_pro_server_api_sdk.api.fingerprint_api import FingerprintApi  # noqa: E501
from fingerprint_pro_server_api_sdk.rest import KnownApiException, ApiException
from urllib.parse import urlencode
from collections import Counter

API_KEY = 'private_key'

VERSION = '8.11.0'

MOCK_GET_VISITORS_200_LIMIT_1 = 'get_visitors_200_limit_1.json'
MOCK_GET_VISITORS_200_LIMIT_500 = 'get_visitors_200_limit_500.json'

MOCK_GET_VISITORS_ERROR_403 = 'get_visitors_403_forbidden.json'
MOCK_GET_VISITORS_ERROR_429 = 'get_visitors_429_too_many_requests.json'

MOCK_DELETE_VISITORS_400_EMPTY_VISITOR_ID = '400_visitor_id_required.json'  # errors/
MOCK_DELETE_VISITORS_400_INCORRECT_VISITOR_ID = '400_visitor_id_invalid.json'  # errors/
MOCK_DELETE_VISITORS_403_FEATURE_NOT_ENABLED = '403_feature_not_enabled.json'  # errors/
MOCK_DELETE_VISITORS_403_TOKEN_NOT_FOUND = '403_token_not_found.json'  # errors/
MOCK_DELETE_VISITORS_403_TOKEN_REQUIRED = '403_token_required.json'  # errors/
MOCK_DELETE_VISITORS_403_WRONG_REGION = '403_wrong_region.json'  # errors/
MOCK_DELETE_VISITORS_403_SUBSCRIPTION_NOT_ACTIVE = '403_subscription_not_active.json'  # errors/
MOCK_DELETE_VISITORS_404_VISITOR_NOT_FOUND = '404_visitor_not_found.json'  # errors/
MOCK_DELETE_VISITORS_429_TO_MANY_REQUESTS = '429_too_many_requests.json'  # errors/

MOCK_GET_EVENT_200 = 'get_event_200.json'
MOCK_GET_EVENT_200_BOTD_FAILED_ERROR = 'get_event_200_botd_failed_error.json'
MOCK_GET_EVENT_200_TOO_MANY_REQUESTS_ERROR = 'get_event_200_too_many_requests_error.json'
MOCK_GET_EVENT_200_IDENTIFICATION_FAILED_ERROR = 'get_event_200_identification_failed_error.json'
MOCK_GET_EVENT_200_ALL_ERRORS = 'get_event_200_all_errors.json'
MOCK_GET_EVENT_200_BROKEN_FORMAT = 'get_event_200_with_broken_format.json'

MOCK_GET_EVENT_403_TOKEN_REQUIRED = '403_token_required.json'  # errors/
MOCK_GET_EVENT_403_TOKEN_NOT_FOUND = '403_token_not_found.json'  # errors/
MOCK_GET_EVENT_403_WRONG_REGION = '403_wrong_region.json'  # errors/
MOCK_GET_EVENT_404 = '404_request_not_found.json'  # errors/

MOCK_UPDATE_EVENT_400 = '400_request_body_invalid.json'  # errors/
MOCK_UPDATE_EVENT_403_TOKEN_REQUIRED = '403_token_required.json'  # errors/
MOCK_UPDATE_EVENT_403_TOKEN_NOT_FOUND = '403_token_not_found.json'  # errors/
MOCK_UPDATE_EVENT_403_WRONG_REGION = '403_wrong_region.json'  # errors/
MOCK_UPDATE_EVENT_404 = '404_request_not_found.json'  # errors/
MOCK_UPDATE_EVENT_409 = '409_state_not_ready.json'  # errors/

MOCK_GET_RELATED_VISITORS_200 = 'related-visitors/get_related_visitors_200.json'
MOCK_GET_RELATED_VISITORS_400 = '400_visitor_id_invalid.json'  # errors/
MOCK_GET_RELATED_VISITORS_403 = '403_feature_not_enabled.json'  # errors/
MOCK_GET_RELATED_VISITORS_404 = '404_visitor_not_found.json'  # errors/
MOCK_GET_RELATED_VISITORS_429 = '429_too_many_requests.json'  # errors/

MOCK_SEARCH_EVENTS_200 = 'get_event_search_200.json'
MOCK_SEARCH_EVENTS_400 = '400_ip_address_invalid.json' # errors/
MOCK_SEARCH_EVENTS_403 = '403_feature_not_enabled.json' # errors/

class MockPoolManager(object):

    def __init__(self, tc, request_headers=None):
        if request_headers is None:
            request_headers = {}
        self._tc = tc
        self._reqs = []
        self.request_headers = request_headers

    def expect_request(self, *args, **kwargs):
        self._reqs.append((args, kwargs))

    @staticmethod
    def get_mock_from_path(path):
        return path.split('/')[-1]

    def request(self, *args, **kwargs):
        self._tc.assertTrue(len(self._reqs) > 0)
        (request_method, request_url), request_config = self._reqs.pop(0)
        status = 200
        if request_config.get('status') is not None:
            status = request_config.get('status')
            request_config.pop('status')

        if request_config.get('method') != 'GET':
            request_path = request_url.split('?')[0]
        else:
            request_path = request_url

        self._tc.maxDiff = None
        self._tc.assertEqual(request_method, args[0])
        self._tc.assertEqual(request_url, args[1])

        self._tc.assertEqual(set(request_config.keys()), set(kwargs.keys()))
        if 'fields' in kwargs and 'fields' in request_config:
            self._tc.assertEqual(Counter(kwargs['fields']), Counter(request_config['fields']))

        # TODO Add support for more complex paths?
        mock_file_by_first_argument = MockPoolManager.get_mock_from_path(request_path)

        if mock_file_by_first_argument == 'bad_text_data':
            return urllib3.HTTPResponse(status=200, body='really bad data')
        if mock_file_by_first_argument == 'bad_json_data':
            return urllib3.HTTPResponse(status=200, body='{}')
        if mock_file_by_first_argument == 'empty_event_answer':
            return urllib3.HTTPResponse(status=200, body='{"products": {}}')
        if mock_file_by_first_argument == 'delete_visitor':
            return urllib3.HTTPResponse(status=200, body='OK')
        if mock_file_by_first_argument == 'update_event':
            return urllib3.HTTPResponse(status=200, body='OK')
        try:
            if mock_file_by_first_argument == 'related-visitors':
                # Extract file name from visitor_id param
                mock_file_by_first_argument = request_config['fields'][1][1]
            if mock_file_by_first_argument == 'search':
                if status == 200:
                    mock_file_by_first_argument = MOCK_SEARCH_EVENTS_200
                else:
                    mock_file_by_first_argument = request_config['fields'][2][1]

            path = './test/mocks/' + mock_file_by_first_argument

            if not os.path.isfile(path):
                path = './test/mocks/errors/' + mock_file_by_first_argument

            with io.open(path, 'r', encoding='utf-8') as mock_file:
                answer_mock = mock_file.read()
                mock_file.close()
            return urllib3.HTTPResponse(status=status, body=answer_mock, headers=self.request_headers)
        except IOError as e:
            print(e)
            return urllib3.HTTPResponse(status=200,
                                        body='{"visitorId": "%s", "visits": []}' % mock_file_by_first_argument)
            pass


class TestFingerprintApi(unittest.TestCase):
    """FingerprintApi unit test stubs"""

    def setUp(self):
        configuration = Configuration(api_key=API_KEY, region="us")
        self.api = FingerprintApi(configuration)  # noqa: E501
        self.integration_info = ('ii', 'fingerprint-pro-server-python-sdk/%s' % VERSION)
        self.request_headers = {
            'Content-Type': 'application/json',
            'Auth-API-Key': 'private_key',
            'Accept': 'application/json',
            'User-Agent': 'Swagger-Codegen/%s/python' % VERSION
        }

    def tearDown(self):
        del self.api
        pass

    @staticmethod
    def get_visitors_path(visitor_id, region='us'):
        domain = {
            "us": "api.fpjs.io",
            "eu": "eu.api.fpjs.io",
            "ap": "ap.api.fpjs.io",
        }.get(region, "api.fpjs.io")
        return 'https://%s/visitors/%s' % (domain, visitor_id)

    @staticmethod
    def get_events_path(request_id, region='us'):
        domain = {
            "us": "api.fpjs.io",
            "eu": "eu.api.fpjs.io",
            "ap": "ap.api.fpjs.io",
        }.get(region, "api.fpjs.io")
        return 'https://%s/events/%s' % (domain, request_id)

    @staticmethod
    def get_related_visitors_path(region='us'):
        domain = {
            "us": "api.fpjs.io",
            "eu": "eu.api.fpjs.io",
            "ap": "ap.api.fpjs.io",
        }.get(region, "api.fpjs.io")
        return 'https://%s/related-visitors' % domain

    @staticmethod
    def get_search_events_path(region='us'):
        domain = {
            "us": "api.fpjs.io",
            "eu": "eu.api.fpjs.io",
            "ap": "ap.api.fpjs.io",
        }.get(region, "api.fpjs.io")
        return 'https://%s/events/search' % domain

    def test_get_visits_correct_data(self):
        """Test checks correct code run result in default scenario"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mocks = [MOCK_GET_VISITORS_200_LIMIT_1,
                 MOCK_GET_VISITORS_200_LIMIT_500
                 ]

        for mock_file in mocks:
            mock_pool.expect_request('GET',
                                     TestFingerprintApi.get_visitors_path(visitor_id=mock_file),
                                     fields=[self.integration_info], headers=self.request_headers,
                                     preload_content=True, timeout=None)

        for mock_file in mocks:
            self.api.get_visits(mock_file)

    def test_get_visits_error_403(self):
        """Test checks correct code run result in case of 403 error for get_visits method"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_visitors_path(visitor_id=MOCK_GET_VISITORS_ERROR_403),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None, status=403)
        with self.assertRaises(KnownApiException) as context:
            self.api.get_visits(MOCK_GET_VISITORS_ERROR_403)
        self.assertEqual(context.exception.status, 403)
        structured_error = context.exception.structured_error
        self.assertIsInstance(structured_error, ErrorPlainResponse)

    def test_get_visits_error_429(self):
        """Test checks correct code run result in case of 429 error for get_visits method"""
        mock_pool = MockPoolManager(self, request_headers={'Retry-After': '4'})
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_visitors_path(visitor_id=MOCK_GET_VISITORS_ERROR_429),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None, status=429)
        with self.assertRaises(KnownApiException) as context:
            self.api.get_visits(MOCK_GET_VISITORS_ERROR_429)
        self.assertEqual(context.exception.status, 429)
        self.assertIsInstance(context.exception.structured_error, ErrorPlainResponse)
        self.assertEqual(context.exception.structured_error.retry_after, 4)

    def test_get_visits_error_429_empty_retry_after(self):
        """Test checks retry after value in exception in case of 429 error for get_visits method"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_visitors_path(visitor_id=MOCK_GET_VISITORS_ERROR_429),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None, status=429)
        with self.assertRaises(KnownApiException) as context:
            self.api.get_visits(MOCK_GET_VISITORS_ERROR_429)
        self.assertEqual(context.exception.status, 429)
        self.assertIsInstance(context.exception.structured_error, ErrorPlainResponse)
        self.assertEqual(context.exception.structured_error.retry_after, 1)

    def test_get_event_correct_data(self):
        """Test checks correct code run result in default scenario"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_events_path(request_id=MOCK_GET_EVENT_200),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None)

        event_response = self.api.get_event(MOCK_GET_EVENT_200)
        event_response_dict = event_response.to_dict()
        self.assertIsNone(event_response_dict["products"]["identification"]["data"]["last_seen_at"]["_global"])
        self.assertIsNone(event_response_dict["products"]["identification"]["data"]["last_seen_at"]["subscription"])
        self.assertIsInstance(event_response.products.raw_device_attributes.data, RawDeviceAttributes)

    def test_get_event_errors_200(self):
        """Test checks correct code run result in scenario of arrors in BotD or identification API"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mocks = [MOCK_GET_EVENT_200_BOTD_FAILED_ERROR,
                 MOCK_GET_EVENT_200_TOO_MANY_REQUESTS_ERROR,
                 MOCK_GET_EVENT_200_IDENTIFICATION_FAILED_ERROR,
                 MOCK_GET_EVENT_200_ALL_ERRORS]

        for mock_file in mocks:
            mock_pool.expect_request('GET',
                                     TestFingerprintApi.get_events_path(request_id=mock_file),
                                     fields=[self.integration_info], headers=self.request_headers,
                                     preload_content=True, timeout=None)

        for mock_file in mocks:
            self.api.get_event(mock_file)

    def test_get_event_error_403(self):
        """Test checks correct code run result in case of 403 error for get_event method"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        test_cases = [
            (MOCK_GET_EVENT_403_TOKEN_REQUIRED, ErrorCode.TOKENREQUIRED),
            (MOCK_GET_EVENT_403_TOKEN_NOT_FOUND, ErrorCode.TOKENNOTFOUND),
            (MOCK_GET_EVENT_403_WRONG_REGION, ErrorCode.WRONGREGION),
        ]

        for (mock_file, error_code) in test_cases:
            mock_pool.expect_request('GET', TestFingerprintApi.get_events_path(request_id=mock_file),
                                     fields=[self.integration_info], headers=self.request_headers,
                                     preload_content=True, timeout=None, status=403)

        for (mock_file, error_code) in test_cases:
            with self.assertRaises(KnownApiException) as context:
                self.api.get_event(mock_file)
            self.assertEqual(context.exception.status, 403)
            self.assertIsInstance(context.exception.structured_error, ErrorResponse)
            self.assertEqual(context.exception.structured_error.error.code, error_code)

    def test_get_event_error_404(self):
        """Test checks correct code run result in case of 404 error for get_event method"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_events_path(request_id=MOCK_GET_EVENT_404),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None, status=404)
        with self.assertRaises(KnownApiException) as context:
            self.api.get_event(MOCK_GET_EVENT_404)
        self.assertEqual(context.exception.status, 404)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.REQUESTNOTFOUND)

    def test_get_event_empty_data(self):
        """Test checks correct code running in case of there is no events"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mocked_id = 'empty_event_answer'
        mock_pool.expect_request('GET', TestFingerprintApi.get_events_path(request_id=mocked_id),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None)

        response = self.api.get_event(mocked_id)
        self.assertIsNotNone(response.products)
        for field in response.products.attribute_map.keys():
            value = getattr(response.products, field)
            self.assertIsNone(value, f"Signal '{field}' is not empty")

    def test_get_visits_empty_answer(self):
        """Test checks correct code running in case of there is no visits"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mocked_id = 'empty_answer'
        mock_pool.expect_request('GET', TestFingerprintApi.get_visitors_path(visitor_id=mocked_id),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None)
        self.assertEqual(self.api.get_visits(mocked_id).visits, [])

    def test_get_visits_bad_data(self):
        """Test checks exception raising when client receives answer with bad data shape"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        test_cases = [
            ('bad_text_data', 'really bad data'),
            ('bad_json_data', '{}')
        ]
        for (mocked_id, raw_data) in test_cases:
            mock_pool.expect_request('GET', TestFingerprintApi.get_visitors_path(visitor_id=mocked_id),
                                     fields=[self.integration_info], headers=self.request_headers,
                                     preload_content=True, timeout=None)
            with self.assertRaises(ApiException) as context:
                self.api.get_visits(mocked_id)
            self.assertEqual(context.exception.status, 200)
            self.assertIsInstance(context.exception.reason, ValueError)
            self.assertEqual(context.exception.body, raw_data)

    def test_init_with_region(self):
        """Test that link for us region generates correct"""
        regions_list = ["us", "eu", "ap"]
        for region in regions_list:
            configuration = Configuration(api_key=API_KEY, region=region)
            del self.api
            self.api = FingerprintApi(configuration)  # noqa: E501
            mock_pool = MockPoolManager(self)
            self.api.api_client.rest_client.pool_manager = mock_pool
            mocked_id = 'empty_answer'
            mock_pool.expect_request('GET',
                                     TestFingerprintApi.get_visitors_path(visitor_id=mocked_id, region=region),
                                     fields=[self.integration_info], headers=self.request_headers,
                                     preload_content=True, timeout=None)
            self.assertEqual(self.api.get_visits(mocked_id).visits, [])

    def test_delete_visitor_data(self):
        """Test that delete visit method works"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mocked_id = 'delete_visitor'
        mock_pool.expect_request('DELETE',
                                 TestFingerprintApi.get_visitors_path(visitor_id=mocked_id) + '?' + urlencode(
                                     [self.integration_info]),
                                 body='{}', headers=self.request_headers, preload_content=True, timeout=None)
        self.api.delete_visitor_data(mocked_id)

    def test_delete_visitor_data_400_error(self):
        """Test that delete visit method returns 400 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        test_cases = [
            (MOCK_DELETE_VISITORS_400_EMPTY_VISITOR_ID, ErrorCode.REQUESTCANNOTBEPARSED),
            (MOCK_DELETE_VISITORS_400_INCORRECT_VISITOR_ID, ErrorCode.REQUESTCANNOTBEPARSED),
        ]

        for (mock_file, error_code) in test_cases:
            mock_pool.expect_request('DELETE',
                                     TestFingerprintApi.get_visitors_path(visitor_id=mock_file) + '?' + urlencode(
                                         [self.integration_info]),
                                     body='{}', headers=self.request_headers, preload_content=True, timeout=None,
                                     status=400)
        for (mock_file, error_code) in test_cases:
            with self.assertRaises(KnownApiException) as context:
                self.api.delete_visitor_data(mock_file)
            self.assertEqual(context.exception.status, 400)
            self.assertIsInstance(context.exception.structured_error, ErrorResponse)
            self.assertEqual(context.exception.structured_error.error.code, error_code)

    def test_delete_visitor_data_403_error(self):
        """Test that delete visit method returns 403 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        test_cases = [
            (MOCK_DELETE_VISITORS_403_FEATURE_NOT_ENABLED, ErrorCode.FEATURENOTENABLED),
            (MOCK_DELETE_VISITORS_403_TOKEN_NOT_FOUND, ErrorCode.TOKENNOTFOUND),
            (MOCK_DELETE_VISITORS_403_TOKEN_REQUIRED, ErrorCode.TOKENREQUIRED),
            (MOCK_DELETE_VISITORS_403_WRONG_REGION, ErrorCode.WRONGREGION),
            (MOCK_DELETE_VISITORS_403_SUBSCRIPTION_NOT_ACTIVE, ErrorCode.SUBSCRIPTIONNOTACTIVE),
        ]

        for (mock_file, error_code) in test_cases:
            mock_pool.expect_request('DELETE',
                                     TestFingerprintApi.get_visitors_path(visitor_id=mock_file) + '?' + urlencode(
                                         [self.integration_info]),
                                     body='{}', headers=self.request_headers, preload_content=True, timeout=None,
                                     status=403)

        for (mock_file, error_code) in test_cases:
            with self.assertRaises(KnownApiException) as context:
                self.api.delete_visitor_data(mock_file)
            self.assertEqual(context.exception.status, 403)
            self.assertIsInstance(context.exception.structured_error, ErrorResponse)
            self.assertEqual(context.exception.structured_error.error.code, error_code)

    def test_delete_visitor_data_404_error(self):
        """Test that delete visit method returns 404 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('DELETE',
                                 TestFingerprintApi.get_visitors_path(
                                     visitor_id=MOCK_DELETE_VISITORS_404_VISITOR_NOT_FOUND) + '?' + urlencode(
                                     [self.integration_info]),
                                 body='{}', headers=self.request_headers, preload_content=True, timeout=None,
                                 status=404)

        with self.assertRaises(KnownApiException) as context:
            self.api.delete_visitor_data(MOCK_DELETE_VISITORS_404_VISITOR_NOT_FOUND)
        self.assertEqual(context.exception.status, 404)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.VISITORNOTFOUND)

    def test_delete_visitor_data_429_error(self):
        """Test that delete visit method returns 429 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('DELETE',
                                 TestFingerprintApi.get_visitors_path(
                                     visitor_id=MOCK_DELETE_VISITORS_429_TO_MANY_REQUESTS) + '?' + urlencode(
                                     [self.integration_info]),
                                 body='{}', headers=self.request_headers, preload_content=True, timeout=None,
                                 status=429)

        with self.assertRaises(KnownApiException) as context:
            self.api.delete_visitor_data(MOCK_DELETE_VISITORS_429_TO_MANY_REQUESTS)
        self.assertEqual(context.exception.status, 429)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.TOOMANYREQUESTS)

    def test_update_event(self):
        """Test that update event method returns 200"""
        test_cases = [
            (EventsUpdateRequest(linked_id='qwe'), '{"linkedId": "qwe"}'),
            (EventsUpdateRequest(tag={'qwe': 123}), '{"tag": {"qwe": 123}}'),
            (EventsUpdateRequest(suspect=False), '{"suspect": false}'),
            (EventsUpdateRequest(suspect=True), '{"suspect": true}'),
            (EventsUpdateRequest(linked_id='qwe', tag={'qwe': 123}, suspect=False),
             '{"linkedId": "qwe", "tag": {"qwe": 123}, "suspect": false}')
        ]

        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_file = 'update_event'

        for (update_body, serialized_body) in test_cases:
            mock_pool.expect_request('PUT',
                                     TestFingerprintApi.get_events_path(request_id=mock_file) + '?' + urlencode(
                                         [self.integration_info]),
                                     headers=self.request_headers, preload_content=True, timeout=None, status=200,
                                     body=serialized_body)

            self.api.update_event(update_body, mock_file)

    def test_update_event_400_error(self):
        """Test that update event method returns 400 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('PUT',
                                 TestFingerprintApi.get_events_path(request_id=MOCK_UPDATE_EVENT_400) + '?' + urlencode(
                                     [self.integration_info]),
                                 headers=self.request_headers, preload_content=True, timeout=None, status=400,
                                 body="{}")

        with self.assertRaises(KnownApiException) as context:
            self.api.update_event({}, MOCK_UPDATE_EVENT_400)
        self.assertEqual(context.exception.status, 400)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.REQUESTCANNOTBEPARSED)

    def test_update_event_403_error(self):
        """Test that delete visit method returns 403 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        test_cases = [
            (MOCK_UPDATE_EVENT_403_TOKEN_REQUIRED, ErrorCode.TOKENREQUIRED),
            (MOCK_UPDATE_EVENT_403_TOKEN_NOT_FOUND, ErrorCode.TOKENNOTFOUND),
            (MOCK_UPDATE_EVENT_403_WRONG_REGION, ErrorCode.WRONGREGION),
        ]

        for (mock_file, error_code) in test_cases:
            mock_pool.expect_request('PUT',
                                     TestFingerprintApi.get_events_path(request_id=mock_file) + '?' + urlencode(
                                         [self.integration_info]),
                                     headers=self.request_headers, preload_content=True, timeout=None, status=403,
                                     body="{}")

        for (mock_file, error_code) in test_cases:
            with self.assertRaises(KnownApiException) as context:
                self.api.update_event({}, mock_file)
            self.assertEqual(context.exception.status, 403)
            self.assertIsInstance(context.exception.structured_error, ErrorResponse)
            self.assertEqual(context.exception.structured_error.error.code, error_code)

    def test_update_event_404_error(self):
        """Test that delete visit method returns 404 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('PUT',
                                 TestFingerprintApi.get_events_path(request_id=MOCK_UPDATE_EVENT_404) + '?' + urlencode(
                                     [self.integration_info]),
                                 headers=self.request_headers, preload_content=True, timeout=None, status=404,
                                 body="{}")

        with self.assertRaises(KnownApiException) as context:
            self.api.update_event({}, MOCK_UPDATE_EVENT_404)
        self.assertEqual(context.exception.status, 404)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.REQUESTNOTFOUND)

    def test_update_event_409_error(self):
        """Test that delete visit method returns 409 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('PUT',
                                 TestFingerprintApi.get_events_path(request_id=MOCK_UPDATE_EVENT_409) + '?' + urlencode(
                                     [self.integration_info]),
                                 headers=self.request_headers, preload_content=True, timeout=None, status=409,
                                 body="{}")

        with self.assertRaises(KnownApiException) as context:
            self.api.update_event({}, MOCK_UPDATE_EVENT_409)
        self.assertEqual(context.exception.status, 409)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.STATENOTREADY)

    def test_get_event_wrong_shape(self):
        """Test that get event method returns correct response"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('GET', TestFingerprintApi.get_events_path(request_id=MOCK_GET_EVENT_200_BROKEN_FORMAT),
                                 fields=[self.integration_info], headers=self.request_headers,
                                 preload_content=True, timeout=None)

        with io.open('./test/mocks/' + MOCK_GET_EVENT_200_BROKEN_FORMAT, encoding='utf-8') as raw_file:
            raw_file_data = raw_file.read()
            raw_file.close()

        with self.assertRaises(ApiException) as context:
            self.api.get_event(MOCK_GET_EVENT_200_BROKEN_FORMAT)
        self.assertEqual(context.exception.status, 200)
        self.assertIsInstance(context.exception.reason, ValueError)
        self.assertEqual(context.exception.body, raw_file_data)

    def test_get_related_visitors(self):
        """Test that get related visitors returns correct response"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_related_visitors_path(),
                                 fields=[self.integration_info, ('visitor_id', MOCK_GET_RELATED_VISITORS_200)],
                                 headers=self.request_headers,
                                 preload_content=True, timeout=None)

        response = self.api.get_related_visitors(MOCK_GET_RELATED_VISITORS_200)
        self.assertIsInstance(response, RelatedVisitorsResponse)

    def test_get_related_visitors_400(self):
        """Test that related visitors returns 400 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('GET',
                                 TestFingerprintApi.get_related_visitors_path(),
                                 fields=[self.integration_info, ('visitor_id', MOCK_GET_RELATED_VISITORS_400)],
                                 headers=self.request_headers, preload_content=True, timeout=None, status=400,
                                 )

        with self.assertRaises(KnownApiException) as context:
            self.api.get_related_visitors(MOCK_GET_RELATED_VISITORS_400)
        self.assertEqual(context.exception.status, 400)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.REQUESTCANNOTBEPARSED)

    def test_get_related_visitors_403(self):
        """Test that related visitors returns 403 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('GET',
                                 TestFingerprintApi.get_related_visitors_path(),
                                 fields=[self.integration_info, ('visitor_id', MOCK_GET_RELATED_VISITORS_403)],
                                 headers=self.request_headers, preload_content=True, timeout=None, status=403,
                                 )

        with self.assertRaises(KnownApiException) as context:
            self.api.get_related_visitors(MOCK_GET_RELATED_VISITORS_403)
        self.assertEqual(context.exception.status, 403)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.FEATURENOTENABLED)

    def test_get_related_visitors_404(self):
        """Test that related visitors returns 404 error"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('GET',
                                 TestFingerprintApi.get_related_visitors_path(),
                                 fields=[self.integration_info, ('visitor_id', MOCK_GET_RELATED_VISITORS_404)],
                                 headers=self.request_headers, preload_content=True, timeout=None, status=404,
                                 )

        with self.assertRaises(KnownApiException) as context:
            self.api.get_related_visitors(MOCK_GET_RELATED_VISITORS_404)
        self.assertEqual(context.exception.status, 404)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.VISITORNOTFOUND)

    def test_get_related_visitors_429(self):
        """Test that related visitors returns 429 error"""
        mock_pool = MockPoolManager(self, request_headers={'Retry-After': '4'})
        self.api.api_client.rest_client.pool_manager = mock_pool

        mock_pool.expect_request('GET',
                                 TestFingerprintApi.get_related_visitors_path(),
                                 fields=[self.integration_info, ('visitor_id', MOCK_GET_RELATED_VISITORS_429)],
                                 headers=self.request_headers, preload_content=True, timeout=None, status=429,
                                 )

        with self.assertRaises(KnownApiException) as context:
            self.api.get_related_visitors(MOCK_GET_RELATED_VISITORS_429)
        self.assertEqual(context.exception.status, 429)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.TOOMANYREQUESTS)
        self.assertEqual(context.exception.structured_error.retry_after, 4)

    def test_search_events_only_limit(self):
        """Test that search events returns 200 with only limit param"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_search_events_path(),
                                 fields=[self.integration_info, ('limit', 1)],
                                 headers=self.request_headers, preload_content=True, timeout=None)

        response = self.api.search_events(1)
        self.assertIsInstance(response, SearchEventsResponse)
        event_response = response.events[0]
        self.assertIsInstance(event_response, SearchEventsResponseEvents)
        self.assertIsInstance(event_response.products, Products)
        self.assertIsInstance(event_response.products.raw_device_attributes.data, RawDeviceAttributes)

    def test_search_events_all_params(self):
        """Test that search events returns 200 with all params"""
        base_params = {
            'limit': 100,
            'visitor_id': MOCK_SEARCH_EVENTS_200,
            'bot': 'good',
            'ip_address': '10.0.0.0/24',
            'linked_id': 'some_linked_id',
            'start': 1582299576511,
            'end': 1582299576513,
            'reverse': True,
            'suspect': False,
            'anti_detect_browser': True,
            'cloned_app': True,
            'factory_reset': True,
            'frida': True,
            'jailbroken': True,
            'min_suspect_score': .5,
            'privacy_settings': True,
            'root_apps': True,
            'tampering': True,
            'virtual_machine': True,
            'vpn': True,
            'vpn_confidence': 'medium',
            'emulator': True,
            'incognito': True,
            'ip_blocklist': True,
            'datacenter': True,
            'developer_tools': True,
            'location_spoofing': True,
            'mitm_attack': True,
            'proxy': True,
            'sdk_version': 'testSdkVersion',
            'sdk_platform': 'testSdkPlatform',
            'proximity_id': 'testProximityId',
            'proximity_precision_radius': 10,
        }

        params = base_params.copy()
        params.update({'environment': ['env1', 'env2']})

        multivalue_expected_params = [
            ('environment', 'env1'),
            ('environment', 'env2'),
        ]

        expected_fields = [self.integration_info] + list(base_params.items()) + multivalue_expected_params

        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request(
            'GET',
            TestFingerprintApi.get_search_events_path(),
            fields=expected_fields,
            headers=self.request_headers,
            preload_content=True,
            timeout=None
        )

        response = self.api.search_events(**params)

        self.assertIsInstance(response, SearchEventsResponse)
        event_response = response.events[0]
        self.assertIsInstance(event_response, SearchEventsResponseEvents)
        self.assertIsInstance(event_response.products, Products)
        self.assertIsInstance(event_response.products.raw_device_attributes.data, RawDeviceAttributes)

    def test_search_events_partial_params(self):
        """Test that search events returns 200 with partial params provided"""
        LIMIT = 100
        PAGINATION_KEY = '1741187431959'
        BOT = 'good'
        LINKED_ID = 'some_linked_id'
        START = 1582299576511
        REVERSE = True
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_search_events_path(),
                                 fields=[self.integration_info, ('limit', LIMIT), ('pagination_key', PAGINATION_KEY),
                                         ('visitor_id', MOCK_SEARCH_EVENTS_200), ('bot', BOT),
                                         ('linked_id', LINKED_ID), ('start', START), ('reverse', REVERSE)],
                                 headers=self.request_headers, preload_content=True, timeout=None)

        response = self.api.search_events(LIMIT, visitor_id=MOCK_SEARCH_EVENTS_200, bot=BOT, linked_id=LINKED_ID,
                                          start=START, reverse=REVERSE, pagination_key=PAGINATION_KEY)
        self.assertIsInstance(response, SearchEventsResponse)
        event_response = response.events[0]
        self.assertIsInstance(event_response, SearchEventsResponseEvents)
        self.assertIsInstance(event_response.products, Products)
        self.assertIsInstance(event_response.products.raw_device_attributes.data, RawDeviceAttributes)

    def test_search_events_400(self):
        """Test that search events returns 400 invalid ip address"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_search_events_path(),
                                 fields=[self.integration_info, ('limit', 1), ('visitor_id', MOCK_SEARCH_EVENTS_400)],
                                 headers=self.request_headers, preload_content=True, timeout=None, status=400)

        with self.assertRaises(KnownApiException) as context:
            self.api.search_events(1, visitor_id=MOCK_SEARCH_EVENTS_400)
        self.assertEqual(context.exception.status, 400)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.REQUESTCANNOTBEPARSED)

    def test_search_events_403(self):
        """Test that search events returns 403 feature not enabled"""
        mock_pool = MockPoolManager(self)
        self.api.api_client.rest_client.pool_manager = mock_pool
        mock_pool.expect_request('GET', TestFingerprintApi.get_search_events_path(),
                                 fields=[self.integration_info, ('limit', 1), ('visitor_id', MOCK_SEARCH_EVENTS_403)],
                                 headers=self.request_headers, preload_content=True, timeout=None, status=403)

        with self.assertRaises(KnownApiException) as context:
            self.api.search_events(1, visitor_id=MOCK_SEARCH_EVENTS_403)
        self.assertEqual(context.exception.status, 403)
        self.assertIsInstance(context.exception.structured_error, ErrorResponse)
        self.assertEqual(context.exception.structured_error.error.code, ErrorCode.FEATURENOTENABLED)

if __name__ == '__main__':
    unittest.main()
