from .api_utils import *
from .models import *
from order.models import *


def _require_access_token(params):
    token = api_get_param_str(params, "access_token")
    return UserToken.get_user_by_token(token)


class ApiAccount:
    @staticmethod
    @api_method("account.auth",
                doc="Аутентификация пользователя",
                params=[
                    api_make_param("login", str, "Логин пользователя"),
                    api_make_param("password", str, "Пароль пользователя"),
                ],
                returns="В случае правильных логина и пароля <code>access_token</code>. "
                        "В противном случае объект ошибки.")
    def auth(login, password):
        user = UserToken.auth(login, password)
        token = UserToken.create_token(user)

        return api_make_response({"access_token": token.access_token})

    @staticmethod
    @api_method("account.deauth",
                doc="Удаление токена, дальшейшие вызовы API с этим токеном вернут ошибку невалидного токена",
                params=[
                    API_PARAM_ACCESS_TOKEN,
                ], returns="В случае успеха стандартный код успеха")
    def deauth(access_token):
        UserToken.deauth(access_token.access_token)
        return api_make_response({})

    @staticmethod
    @api_method("account.register",
                doc="Регистрация нового пользователя",
                params=[
                    api_make_param("name", str, "Имя пользователя"),
                    api_make_param("surname", str, "Фамилия пользователя"),
                    api_make_param("phone", str, "Телефон в формате <code>[[+]7]1112223333</code> "
                                                 "<i>(в квадратных скобках необязательная часть)</i>"),
                    api_make_param("email", str, "Почта"),
                    api_make_param("password", str, "Пароль пользователя"),
                ], returns="Аналогично методу <code>account.auth</code> в случае успеха")
    def register(name, surname, phone, email, password):

        user = SiteUser.create_user(
            name=name,
            surname=surname,
            phone=phone,
            email=email,
            password=password
        )

        try:
            user.full_clean()
            user.save()

            try:
                token = UserToken.create_token(user)
                return api_make_response({"access_token": token.access_token})

            except Exception as ex:
                # если вдруг токен нельзя создать
                user.delete()
                raise ex

        except ValidationError as validation_error:
            traceback.print_exc()
            errors = {}
            for field_name in validation_error.error_dict:
                err_list = validation_error.error_dict[field_name]
                print(err_list)
                obj = []
                for err in err_list:
                    obj.append({
                        "code": err.code
                    })
                errors[field_name] = obj
            raise Exception(API_ERROR_USER_REGISTER, errors)

    @staticmethod
    @api_method("account.verifyPhone",
                doc="Запросить верификацию номера телефона."
                    "Если телефон уже верифицирован, метод вернет соответствующую ошибку",
                params=[
                    API_PARAM_ACCESS_TOKEN,
                    api_make_param("code", int, "Код верификации. Если не передать, будет выполнен звонок", False),
                ],
                returns='{"status": "success"}, если верификация пройдена. Иначе одну из стандартных ошибок')
    def verify_phone(access_token, code):
        user = access_token.user

        if user.is_phone_verified:
            raise Exception(API_ERROR_VALIDATION_CURRENTLY_VERIFIED)

        if code is None:
            res, err_code = PhoneVerificationService.send_verify(user.phone)

            if not res:
                if err_code in API_ERROR_VALIDATION:
                    raise Exception(API_ERROR_VALIDATION[err_code])
                else:
                    raise Exception(API_ERROR_VALIDATION_UNKNOWN)

            return api_make_response({"action": "phone_call"})
        else:
            res, err_code = PhoneVerificationService.check_code(user.phone, code)

            if res:
                user.is_phone_verified = True
                user.save()
                return api_make_response({})
            else:
                if err_code in API_ERROR_VALIDATION:
                    raise Exception(API_ERROR_VALIDATION[err_code])
                else:
                    raise Exception(API_ERROR_VALIDATION_UNKNOWN)

    @staticmethod
    @api_method("account.get",
                doc="Получение информации о пользователе",
                params=[
                    API_PARAM_ACCESS_TOKEN,
                ],
                returns="Поля пользователя (name, surname, email, phone, phone_verified).")
    def get(access_token):
        user = access_token.user
        return api_make_response({
            "id": user.id,
            "name": user.name,
            "surname": user.surname,
            "email": user.email,
            "phone": user.phone,
            "phone_verified": user.is_phone_verified
        })


class ApiOrder:
    @staticmethod
    @api_method("order.getForm",
                doc="Получение формы создания заказа в виде полей, которые нужно показать пользователю",
                params=[],
                returns="<code>JSON</code> объект, содержащий данные формы. Структура пока не определена")
    def get_form():
        return api_make_response({
            "fields": [
                # name = models.CharField(max_length=200, verbose_name="Название заказа")
                {
                    "name": "name",
                    "label": "Название заказа",
                    "widget": "text",
                    "attrs": {
                        "max_len": 200
                    },
                    "required": True
                },

                # square = models.DecimalField(max_digits=7, decimal_places=2, blank=False, verbose_name="Площадь в м²")
                {
                    "name": "square",
                    "label": "Площадь в м²",
                    "widget": "decimal",
                    "attrs": {
                        "max_digits": 7,
                        "decimal_places": 2
                    },
                    "required": True
                },

                # work_time = models.CharField(max_length=100, blank=True, verbose_name="Рабочее время")
                {
                    "name": "work_time",
                    "label": "Рабочее время",
                    "widget": "text",
                    "attrs": {
                        "max_len": 100
                    },
                    "required": False
                },

                # type_of_renovation = models.CharField(max_length=10, choices=TYPE_OF_RENOVATION_CHOICES,
                #   default=CHOICE_UNDEFINED, blank=True, verbose_name="Тип ремонта")
                {
                    "name": "type_of_renovation",
                    "label": "Тип ремонта",
                    "widget": "choice",
                    "attrs": {
                        "default": None,
                        "choices": Order.TYPE_OF_RENOVATION_CHOICES
                    },
                    "required": False
                },

                # type_of_room = models.CharField(max_length=10, choices=TYPE_OF_ROOM_CHOICES,
                #   blank=True, default=CHOICE_UNDEFINED, verbose_name="Тип квартиры")
                {
                    "name": "type_of_room",
                    "label": "Тип квартиры",
                    "widget": "radio",
                    "attrs": {
                        "default": None,
                        "choices": Order.TYPE_OF_ROOM_CHOICES
                    },
                    "required": False
                },

                # флажок
                {
                    "name": "is_with_warranty",
                    "label": "С гарантией",
                    "widget": "checkbox",
                    "attrs": {
                        "default": True
                    },
                    "required": False
                },
            ]
        })


def api_call_method(method_name, params: dict):
    try:
        if method_name in api_methods_dict:
            out = api_methods_dict[method_name]["func"](**params)
            if out is None:
                raise Exception(API_ERROR_INTERNAL_ERROR, "method returned null object")
        else:
            raise Exception(API_ERROR_METHOD_NOT_FOUND)
    except Exception as ex:
        traceback.print_exc()
        out = make_error_object(ex)
    return out


def api_get_documentation():
    # {
    # "name": p["name"],
    # "type": p["type"],
    # "description": p["description"],
    # "required": p["required"]
    # }
    return []