from flask_restful import Api, Resource, reqparse
import globals
from src import auth
from webargs.flaskparser import use_args, use_kwargs, parser, abort
from webargs import fields, validate
from bson import json_util, ObjectId
from pymongo import UpdateOne, UpdateMany
import json



boxes_args = reqparse.RequestParser()
boxes_args.add_argument("Authorization", type=str, help="Unauthorize", required=True, location="headers")
boxes_args.add_argument("DataIndexes", type=str, help="Please Provide DataIndexes", required=True)
boxes_args.add_argument("Page", type=int, help="Please Provide Page Number", required=True)

boxesDB = globals.boxesDB
auth_args = {"Authorization": fields.Str(required=True)}
boxData = {
        "fileId": fields.Str(required=True),
        "pageId": fields.Str(required=True),
        # "coordinates": fields.List(fields.Dict(keys=fields.Str(), values=fields.Str())),
        "coordinates": fields.List(fields.Float(required=True)),
        "linesData": fields.List(fields.Dict(required=True)),
        "innerText": fields.Str(required=True),
        "tagName": fields.Str(required=True),
        "linesId": fields.List(fields.Str(required=True)),
        "boxIndex": fields.Integer(required=True)
    }

class NewBox(Resource):


    @use_kwargs(auth_args, location="headers")
    @use_kwargs(boxData)
    def post(self,Authorization,fileId,pageId,coordinates,innerText,tagName,linesId,boxIndex,linesData):
        if auth.verify(str(Authorization).split(" ")[1]):
            id = str(boxesDB.insert_one({
                "fileId": fileId,
                "pageId": pageId,
                "coordinates": coordinates,
                "innerText": innerText,
                "tagName": tagName,
                "linesId": linesId,
                "linesData": linesData,
                "boxIndex": boxIndex
            }).inserted_id)
            return {"msg": id}, 200
        else:
            return {"msg": "Unauthorized! Access Denied"}, 401

class deleteBox(Resource):
    boxId = {"id": fields.Str(required=True)}

    @use_kwargs(auth_args, location="headers")
    @use_kwargs(boxId, location="query")
    def delete(self,Authorization,id):
        if auth.verify(str(Authorization).split(" ")[1]):
            boxesDB.delete_one({"_id": ObjectId(id)})
            return {"msg": "Deleted Successfully"}, 200
        else:
            return {"msg": "Unauthorized! Access Denied"}, 401

class updateBox(Resource):

    boxData["id"] = fields.Str(required=True)
    @use_kwargs(auth_args, location="headers")
    @use_kwargs(boxData)
    def put(self,Authorization,id,fileId,pageId,coordinates,innerText,tagName,linesId,boxIndex,linesData):
        if auth.verify(str(Authorization).split(" ")[1]):
            boxesDB.update_one({
                "_id": ObjectId(id)
            },
                {
                    "$set": {
                        "fileId": fileId,
                        "pageId": pageId,
                        "coordinates": coordinates,
                        "innerText": innerText,
                        "linesData": linesData,
                        "tagName": tagName,
                        "linesId": linesId,
                        "boxIndex": boxIndex
                    }
                }
            )
            return {"msg": "Updated Successfully"}, 200

        else:
            return {"msg": "Unauthorized! Access Denied"}, 401

        

class reArrangeBoxes(Resource):
    reArrangeData = {
        "boxId": fields.Str(required=True),
        "pageId":fields.Str(required=True),
        "oldIndex": fields.Integer(required=True),
        "newIndex": fields.Integer(required=True)
    }
    @use_kwargs(auth_args, location="headers")
    @use_kwargs(reArrangeData)
    def put(self,Authorization, boxId, pageId, oldIndex, newIndex):
        if auth.verify(str(Authorization).split(" ")[1]):
            if oldIndex - newIndex < 0:
                boxesDB.bulk_write(
                    [

                        UpdateMany(
                            {
                                "pageId": pageId,
                                "$and": [
                                    {
                                        "boxIndex":
                                            {
                                                "$gt": oldIndex
                                            }
                                    },
                                    {
                                        "boxIndex":
                                            {
                                                "$lte": newIndex
                                            }
                                    },

                                ]
                            },
                            {
                                "$inc": {
                                    "boxIndex": -1
                                }
                            }
                        ),
                        UpdateOne(
                            {
                                "_id": ObjectId(boxId)
                            },
                            {
                                "$set": {
                                    "boxIndex": newIndex
                                }
                            }
                        )
                    ]
                )
            else:
                boxesDB.bulk_write(
                    [

                        UpdateMany(
                            {
                                "pageId": pageId,
                                "$and": [
                                    {
                                        "boxIndex":
                                            {
                                                "$gte": newIndex
                                            }
                                    },
                                    {
                                        "boxIndex":
                                            {
                                                "$lt": oldIndex
                                            }
                                    },

                                ]
                            },
                            {
                                "$inc": {
                                    "boxIndex": 1
                                }
                            }
                        ),
                        UpdateOne(
                            {
                                "_id": ObjectId(boxId)
                            },
                            {
                                "$set": {
                                    "boxIndex": newIndex
                                }
                            }
                        )
                    ]
                )
            return {"msg": "Updated Successfully"}, 200
        else:
            return {"msg": "Unauthorized! Access Denied"}, 401


# This error handler is necessary for usage with Flask-RESTful
@parser.error_handler
def handle_request_parsing_error(err, req, schema, *, error_status_code, error_headers):
    """webargs error handler that uses Flask-RESTful's abort function to return
    a JSON error response to the client.
    """
    if not error_status_code:
        abort(400, errors=err.messages)
    else:
        abort(error_status_code, errors=err.messages)
