from uuid import UUID
import phonenumbers
from phonenumbers import carrier

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from .errors import ValidationError

ERROR_CODES = [
    {
        "http_code": 409,
        "response_code": None,
        "description": "Duplicated Reference Id. Cannot create new resource",
        "error_type": "generic"

    },
    {
        "http_code": 404,
        "response_code": None,
        "description": "Reference Id not found. Requested resource does not exist",
        "error_type": "generic"

    },
    {
        "http_code": 400,
        "response_code": None,
        "description": "Bad request. Request does not follow the specification.",
        "error_type": "generic"

    },
    {
        "http_code": 401,
        "response_code": None,
        "description": "Authentication failed. Credentials not valid",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "NOT_ALLOWED",
        "description": "Authorization failed. User does not have permission.",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "NOT_ALLOWED_TARGET_ENVIRONMENT",
        "description": "Not allowed target environment",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "INVALID_CALLBACK_URL_HOST",
        "description": "Callback URL with different host name then configured for API User",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "INVALID_CURRENCY",
        "description": "Currency not supported on the requested account",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "INTERNAL_PROCESSING_ERROR",
        "description": "Default error code used when there is no specific error mapping.",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "SERVICE_UNAVAILABLE",
        "description": "Service temporary unavailable, try again later",
        "error_type": "generic"

    },

    {
        "http_code": 500,
        "response_code": "PAYER_NOT_FOUND",
        "description": "Payer not found",
        "error_type": "preapproval"

    },

    {
        "http_code": 500,
        "response_code": "PAYEE_NOT_ALLOWED_TO_RECEIVE",
        "description": "Payee cannot receive funds due to e.g. transfer limit.",
        "error_type": "request_to_pay"

    },

    {
        "http_code": 500,
        "response_code": "NOT_ENOUGH_FUNDS",
        "description": "Not enough funds on payer account",
        "error_type": "transfer"

    },
    {
        "http_code": 500,
        "response_code": "PAYER_LIMIT_REACHED",
        "description": "Not allowed to end due to Payer limit reached",
        "error_type": "transfer"

    },

    {
        "http_code": 500,
        "response_code": "PAYEE_NOT_FOUND",
        "description": "Payee not found. Account holder is not registered",
        "error_type": "transfer"

    },

    {
        "http_code": 404,
        "response_code": None,
        "description": "Account holder is not found",
        "error_type": "account"

    }
]


def requests_retry_session(
    retries=3,
    backoff_factor=0.3,
    status_forcelist=(502, 504),
    session=None,
    **kwargs
):
    session = session or requests.Session()
    retry = Retry(
        total=retries,
        read=retries,
        connect=retries,
        backoff_factor=backoff_factor,
        status_forcelist=status_forcelist,
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    return session


def validate_phone_number(number):
    obj = phonenumbers.parse(number, "UG")
    if not phonenumbers.is_valid_number(obj):
        raise ValidationError("Invalid Phone number {0}".format(number))
    if (carrier.name_for_number(obj, "en") != "MTN"):
        raise ValidationError(
            "{0}: Only MTN is supported at the moment".format(number))
    return "256{0}".format(obj.national_number)


def validate_number(number):
    number_types = (int, float)
    if not type(number) in number_types:
        raise ValidationError("{0}: Must be a number".format(number))
    return number


def validate_string(_string):
    if not isinstance(_string, str):
        raise ValidationError("{0}: Must be a string".format(_string))
    return _string


def validate_uuid(_string):
    try:
        UUID(_string, version=4)
    except ValueError:
        raise ValidationError(
            "{0}: Must be a valid uuid4 string".format(_string))
    return _string