class RecordView(HTTPMethodView):
    decorators = [auth.authorized()]

    async def get(self, request, attribute, id, technical):
        try:
            with await request.app.redis as redis:
                curr_record = Record(attribute, merchant=technical['merchant'], industry=technical['industry'],
                                     id=id, redis_conn=redis)
                data = await curr_record.get_record()
            if data['created_at'] is None:
                return json({
                    "message": "Record with such value is not existed."
                }, status=404)
            else:
                return json(data, status=200)
        except Exception:
            capture_exception()
            return json({"message": "Internal Server error"
                         }, status=500)

    async def delete(self, request, attribute, id, technical):
        try:
            with await request.app.redis as redis:
                curr_record = Record(attribute, merchant=technical['merchant'], industry=technical['industry'],
                                     id=id, redis_conn=redis)
                await curr_record.delete_record()
            return json(
                {"message": "Item was deleted from the list"}, status=204)
        except Exception:
            capture_exception()
            return json({"message": "Internal Server error"
                     }, status=500)
class MerchantCreateView(HTTPMethodView):
    decorators = [
        auth.authorized(roles=['global', 'admin']),
        validate_json(schema_merchant, methods=['POST'])
    ]

    async def post(self, request, technical):
        try:
            merchant_names = [
                x['name'] for x in await merchant.Merchant.list_merchants(
                    redis_conn=request.app.redis)
            ]
            data = request.json
            if data['name'] not in merchant_names:
                data['created_at'] = datetime.now().strftime(
                    "%Y-%m-%d %H:%M:%S")
                with await request.app.redis as redis:
                    new_merchant = merchant.Merchant(redis_conn=redis)
                    merchant_id, data = await new_merchant.create_merchant(data
                                                                           )
                data['merchant_id'] = int(merchant_id)
                return json(data, status=201)
            else:
                return json(
                    {
                        "message":
                        "Merchant with the name {0} already exists.".format(
                            data['name'])
                    },
                    status=409)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)
class RecordListView(HTTPMethodView):
    decorators = [auth.authorized(), validate_json(schema_list)]

    async def post(self, request, attribute, technical):
        try:
            if request.json['merchant_id'] is None and technical['role'] in ['global', 'admin']:
                return json({"message": "User with role global should include merchant id in request body."
                         }, status=400)
            else:
                merchant_id = request.json['merchant_id']
            with await request.app.redis as redis:
                if technical['role'] == 'global':
                    merch_search = Merchant(id=merchant_id, redis_conn=redis)
                    id, merch_info = await merch_search.get_merchant()
                    if id is None:
                        return json({
                                     "message": "No such merchant."
                                    }, status=400)
                    merchant_name = merch_info['name']
                    merchant_industry = merch_info['industry']
                else:
                    merchant_name = technical['merchant']
                    merchant_industry = technical['industry']
                record_list = RecordList(attribute=attribute, industry=merchant_industry,
                                         merchant=merchant_name, redis_conn=redis)
                data = await record_list.list_records()
                data = sorted(data, key=lambda x: x['record_id'], reverse=True)
            return json({
                "attribute": attribute,
                "records": data
            }, status=201)
        except Exception:
            capture_exception()
            return json({"message": "Internal Server error"
                         }, status=500)
class UserListView(HTTPMethodView):
    decorators = [auth.authorized(roles=['global', 'admin'])]

    async def get(self, request, technical):
        try:
            with await request.app.redis as redis:
                data = await user.User.list_users(redis_conn=redis)
                data = sorted(data, key=lambda x: x['user_id'], reverse=True)
            return json(data, status=200)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)
class IndustrieListView(HTTPMethodView):
    decorators = [auth.authorized()]

    async def get(self, request, technical):
        try:
            with await request.app.redis as redis:
                ind_list = await Industry.list_industries(redis_conn=redis)
                print(ind_list)
                return json({"industries": ind_list}, status=200)
        except Exception as e:
            capture_exception()
            return json({"message": "Internal Server error"}, status=500)
class MerchantView(HTTPMethodView):
    decorators = [
        auth.authorized(roles=['global', 'admin']),
        validate_json(schema_merchant, methods=['POST'])
    ]

    async def get(self, request, id, technical):
        try:
            with await request.app.redis as redis:
                curr_merchant = merchant.Merchant(id=id, redis_conn=redis)
                curr_merchant_id, data = await curr_merchant.get_merchant()
            if data is not None:
                data['merchant_id'] = curr_merchant_id
                return json(data, status=200)
            else:
                return json({"message": "Merchant is not existed yet."},
                            status=404)
        except Exception as e:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)

    async def post(self, request, id, technical):
        try:
            with await request.app.redis as redis:
                curr_merchant = merchant.Merchant(id=id, redis_conn=redis)
                data = request.json
                id, old_data = await curr_merchant.get_merchant()
                data['created_at'] = old_data['created_at']
                merchant_id, upd_merchant_data = await curr_merchant.update_merchant(
                    data)
                data['merchant_id'] = merchant_id
            if upd_merchant_data is not None:
                return json(upd_merchant_data, status=200)
            else:
                return json({"message": "Merchant is not existed yet."},
                            status=404)
        except Exception:
            capture_exception()
            return json({"message": "Internal Server error"}, status=500)

    async def delete(self, request, id, technical):
        try:
            with await request.app.redis as redis:
                curr_merchant = merchant.Merchant(id=id, redis_conn=redis)
                await curr_merchant.delete_merchant()
            return json({"message": "There is no such merchant anymore."},
                        status=204)
        except Exception:
            capture_exception()
            return json({"message": "Internal Server error"}, status=500)
class UserCreateView(HTTPMethodView):
    decorators = [
        auth.authorized(roles=['global', 'admin']),
        validate_json(schema_user)
    ]

    async def post(self, request, technical):
        try:
            data = request.json
            logins = [
                x['login'] for x in await user.User.list_users(
                    redis_conn=request.app.redis)
            ]
            if data['login'] not in logins:
                data['created_at'] = datetime.now().strftime(
                    "%Y-%m-%d %H:%M:%S")
                with await request.app.redis as redis:
                    curr_merchant = merchant.Merchant(id=data['merchant_id'],
                                                      redis_conn=redis)
                    curr_merchant_id, result = await curr_merchant.get_merchant(
                    )
                    if curr_merchant_id is not None:
                        new_user = user.User(redis_conn=redis)
                        data['user_id'], data[
                            'token'] = await new_user.create_user(data=data)
                        return json(data, status=201)
                    else:
                        return json({"message": "There is no such merchant."},
                                    status=400)
            else:
                return json(
                    {
                        "message":
                        "User with the login {0} already exists.".format(
                            data['login'])
                    },
                    status=409)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)
class RecordCreateView(HTTPMethodView):
    decorators = [auth.authorized(), validate_json(schema_record)]

    async def post(self, request, attribute, technical):
        try:
            if request.json['merchant_id'] is None and technical['role'] in ['global', 'admin']:
                return json({"message": "User with role global should include merchant id in request body."
                         }, status=400)
            body = request.json
            data = dict()
            data['list'] = body['list']
            data['ttl'] = body['ttl']
            data['created_at'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            data['added_by'] = technical['role']
            with await request.app.redis as redis:
                if technical['role'] == 'global':
                    merch_search = Merchant(id=body['merchant_id'], redis_conn=redis)
                    id, merch_info = await merch_search.get_merchant()
                    if id is None:
                        return json({
                                     "message": "No such merchant."
                                    }, status=400)
                    merchant_name = merch_info['name']
                    merchant_industry = merch_info['industry']
                else:
                    merchant_name = technical['merchant']
                    merchant_industry = technical['industry']
                new_record = Record(attribute, industry=merchant_industry, merchant=merchant_name,
                                    value=body["value"], redis_conn=redis)
                result = await new_record.create_record(data)
            if result is not None:
                return json(result, status=201)
            else:
                return json({
                    "message": "Record with such value is already existed."
                }, status=409)
        except Exception as e:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"
                     }, status=500)
class RecordListAddView(HTTPMethodView):
    decorators = [auth.authorized(), validate_json(schema_listadd, methods=['POST'])]

    async def post(self, request, attribute, technical):
        try:
            if request.json['merchant_id'] is None and technical['role'] in ['global', 'admin']:
                return json({"message": "User with role global should include merchant id in request body."
                         }, status=400)
            data = request.json
            merchant_id = data.pop('merchant_id')

            for rec in data['batch']:
                rec['created_at'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                rec['added_by'] = technical['role']
            with await request.app.redis as redis:
                if technical['role'] == 'global':
                    merch_search = Merchant(id=merchant_id, redis_conn=redis)
                    id, merch_info = await merch_search.get_merchant()
                    if id is None:
                        return json({
                                     "message": "No such merchant."
                                    }, status=400)
                    merchant_name = merch_info['name']
                    merchant_industry = merch_info['industry']
                else:
                    merchant_name = technical['merchant']
                    merchant_industry = technical['industry']
                new_record_list = RecordList(attribute=attribute, industry=merchant_industry,
                                             merchant=merchant_name, redis_conn=redis)
                result = await new_record_list.add_records(data)
            return json({
                "attribute": attribute,
                "records": result
            }, status=201)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"
                     }, status=500)
class UserView(HTTPMethodView):
    decorators = [
        auth.authorized(roles=['global', 'admin']),
        validate_json(schema_user, methods=['PUT'])
    ]

    async def get(self, request, id, technical):
        try:
            with await request.app.redis as redis:
                curr_user = user.User(id=id, redis_conn=redis)
                data = await curr_user.get_user()
            if data is not None:
                data.pop('password')
                data['user_id'] = int(id)
                data['merchant_id'] = int(data['merchant_id'])
                return json(data, status=200)
            else:
                return json({"message": "User is not existed yet."},
                            status=404)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)

    async def put(self, request, id, technical):
        try:
            data = request.json
            with await request.app.redis as redis:
                curr_user = user.User(id=id, redis_conn=redis)
                old_data = await curr_user.get_user()
                data['created_at'] = old_data['created_at']
                data['user_id'], data['token'] = await curr_user.update_user(
                    data)
            if data['user_id'] is not None:
                return json(data, status=201)
            else:
                return json({"message": "User is not existed yet."},
                            status=404)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)

    async def delete(self, request, id, technical):
        try:
            with await request.app.redis as redis:
                curr_user = user.User(id=id, redis_conn=redis)
                await curr_user.delete_user()
            return json({"message": "There is no such user anymore."},
                        status=204)
        except Exception:
            capture_exception()
            return json({"message": "Internal Server error"}, status=500)

    async def patch(self, request, id, technical):
        try:
            with await request.app.redis as redis:
                curr_user = user.User(id=id, redis_conn=redis)
                token = await curr_user.refresh()
            if token is not None:
                return json({"user_id": int(id), "token": token}, status=200)
            else:
                return json({"message": "User is not existed yet."},
                            status=404)
        except Exception as e:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"}, status=500)
class RecordPaymentView(HTTPMethodView):
    decorators = [auth.authorized(), validate_json(schema_payment_put, methods=['PUT']), validate_json(schema_payment, methods=['POST'])]

    async def post(self, request, technical):
        try:
            values = request.json
            order_id = values.pop('order_id')
            with await request.app.redis as redis:
                if technical['list_level'] == 'Local':
                    payment_check = Payment(industry=technical['industry'], merchant=technical['merchant'], redis_conn=redis)
                    result = await payment_check.find_payment(values)
                elif technical['list_level'] == 'Industry':
                    payment_check = Payment(industry=technical['industry'], merchant='*', redis_conn=redis)
                    result = await payment_check.find_payment(values)
                else:
                    payment_check = Payment(industry='*', merchant='*', redis_conn=redis)
                    result = await payment_check.find_payment(values)
            if 'whitelist' in result.values():
                return json({
                        "order_id": order_id,
                        "score": 100,
                        "group": "positive",
                        "description": result
                }, status=200)
            elif 'greylist' in result.values():
                return json({
                        "order_id": order_id,
                        "score": -20,
                        "group": "low",
                        "description": result
                }, status=200)
            elif 'blacklist' in result.values():
                return json({
                        "order_id": order_id,
                        "score": -100,
                        "group": "high",
                        "description": result
                }, status=200)
            else:
                return json({
                    "order_id": order_id,
                    "score": 0,
                    "group": "neutral",
                    "description": result
                }, status=200)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"
                         }, status=500)

    async def put(self, request, technical):
        try:
            data = request.json
            for key in data.keys():
                data[key]['created_at'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                data[key]['added_by'] = technical['role']
            with await request.app.redis as redis:
                payment_data = Payment(industry=technical['industry'], merchant=technical['merchant'], redis_conn=redis)
                result = await payment_data.add_payment(data)
            return json(result, status=201)
        except Exception:
            capture_exception(data=request.json)
            return json({"message": "Internal Server error"
                         }, status=500)