from flask import session, request, abort
from flask_restplus import Namespace, Resource
from CTFd.models import (
    db,
    Users,
    Solves,
    Awards,
    Tracking,
    Unlocks,
    Submissions,
    Notifications,
)
from CTFd.utils.decorators import authed_only, admins_only, ratelimit
from CTFd.cache import clear_standings
from CTFd.utils.user import get_current_user, is_admin
from CTFd.utils.decorators.visibility import (
    check_account_visibility,
    check_score_visibility,
)

from CTFd.schemas.submissions import SubmissionSchema
from CTFd.schemas.awards import AwardSchema
from CTFd.schemas.users import UserSchema


users_namespace = Namespace("users", description="Endpoint to retrieve Users")


@users_namespace.route("")
class UserList(Resource):
    @check_account_visibility
    def get(self):
        users = Users.query.filter_by(banned=False, hidden=False)
        response = UserSchema(view="user", many=True).dump(users)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        return {"success": True, "data": response.data}

    @admins_only
    def post(self):
        req = request.get_json()
        schema = UserSchema("admin")
        response = schema.load(req)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        db.session.add(response.data)
        db.session.commit()

        if request.args.get("notify"):
            name = response.data.name
            password = req.get("password")

        clear_standings()

        response = schema.dump(response.data)

        return {"success": True, "data": response.data}


@users_namespace.route("/<int:user_id>")
@users_namespace.param("user_id", "User ID")
class UserPublic(Resource):
    @check_account_visibility
    def get(self, user_id):
        user = Users.query.filter_by(id=user_id).first_or_404()

        if (user.banned or user.hidden) and is_admin() is False:
            abort(404)

        response = UserSchema(view=session.get("type", "user")).dump(user)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        response.data["place"] = user.place
        response.data["score"] = user.score

        return {"success": True, "data": response.data}

    @admins_only
    def patch(self, user_id):
        user = Users.query.filter_by(id=user_id).first_or_404()
        data = request.get_json()
        data["id"] = user_id
        schema = UserSchema(view="admin", instance=user, partial=True)
        response = schema.load(data)
        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        db.session.commit()

        response = schema.dump(response.data)

        db.session.close()

        clear_standings()

        return {"success": True, "data": response}

    @admins_only
    def delete(self, user_id):
        Notifications.query.filter_by(user_id=user_id).delete()
        Awards.query.filter_by(user_id=user_id).delete()
        Unlocks.query.filter_by(user_id=user_id).delete()
        Submissions.query.filter_by(user_id=user_id).delete()
        Solves.query.filter_by(user_id=user_id).delete()
        Tracking.query.filter_by(user_id=user_id).delete()
        Users.query.filter_by(id=user_id).delete()
        db.session.commit()
        db.session.close()

        clear_standings()

        return {"success": True}


@users_namespace.route("/me")
class UserPrivate(Resource):
    @authed_only
    def get(self):
        user = get_current_user()
        response = UserSchema("self").dump(user).data
        response["place"] = user.place
        response["score"] = user.score
        return {"success": True, "data": response}

    @authed_only
    def patch(self):
        user = get_current_user()
        data = request.get_json()
        schema = UserSchema(view="self", instance=user, partial=True)
        response = schema.load(data)
        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        db.session.commit()

        response = schema.dump(response.data)
        db.session.close()

        clear_standings()

        return {"success": True, "data": response.data}


@users_namespace.route("/me/solves")
class UserPrivateSolves(Resource):
    @authed_only
    def get(self):
        user = get_current_user()
        solves = user.get_solves(admin=True)

        view = "user" if not is_admin() else "admin"
        response = SubmissionSchema(view=view, many=True).dump(solves)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        return {"success": True, "data": response.data}


@users_namespace.route("/me/fails")
class UserPrivateFails(Resource):
    @authed_only
    def get(self):
        user = get_current_user()
        fails = user.get_fails(admin=True)

        view = "user" if not is_admin() else "admin"
        response = SubmissionSchema(view=view, many=True).dump(fails)
        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        if is_admin():
            data = response.data
        else:
            data = []
        count = len(response.data)

        return {"success": True, "data": data, "meta": {"count": count}}


@users_namespace.route("/me/awards")
@users_namespace.param("user_id", "User ID")
class UserPrivateAwards(Resource):
    @authed_only
    def get(self):
        user = get_current_user()
        awards = user.get_awards(admin=True)

        view = "user" if not is_admin() else "admin"
        response = AwardSchema(view=view, many=True).dump(awards)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        return {"success": True, "data": response.data}


@users_namespace.route("/<user_id>/solves")
@users_namespace.param("user_id", "User ID")
class UserPublicSolves(Resource):
    @check_account_visibility
    @check_score_visibility
    def get(self, user_id):
        user = Users.query.filter_by(id=user_id).first_or_404()

        if (user.banned or user.hidden) and is_admin() is False:
            abort(404)

        solves = user.get_solves(admin=is_admin())

        view = "user" if not is_admin() else "admin"
        response = SubmissionSchema(view=view, many=True).dump(solves)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        #  return {"success": True, "data": response.data}
        return {"success": True, "data": None}


@users_namespace.route("/<user_id>/fails")
@users_namespace.param("user_id", "User ID")
class UserPublicFails(Resource):
    @check_account_visibility
    @check_score_visibility
    def get(self, user_id):
        user = Users.query.filter_by(id=user_id).first_or_404()

        if (user.banned or user.hidden) and is_admin() is False:
            abort(404)
        fails = user.get_fails(admin=is_admin())

        view = "user" if not is_admin() else "admin"
        response = SubmissionSchema(view=view, many=True).dump(fails)
        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        if is_admin():
            data = response.data
        else:
            data = []
        count = len(response.data)

        #  return {"success": True, "data": data, "meta": {"count": count}}
        return {"success": True, "data": None, "meta": {"count": None}}


@users_namespace.route("/<user_id>/awards")
@users_namespace.param("user_id", "User ID or 'me'")
class UserPublicAwards(Resource):
    @check_account_visibility
    @check_score_visibility
    def get(self, user_id):
        user = Users.query.filter_by(id=user_id).first_or_404()

        if (user.banned or user.hidden) and is_admin() is False:
            abort(404)
        awards = user.get_awards(admin=is_admin())

        view = "user" if not is_admin() else "admin"
        response = AwardSchema(view=view, many=True).dump(awards)

        if response.errors:
            return {"success": False, "errors": response.errors}, 400

        #  return {"success": True, "data": response.data}
        return {"success": True, "data": None}

[(26, 'flask_restplus.Namespace', 'Namespace', (['"""users"""'], {'description': '"""Endpoint to retrieve Users"""'}), False, 'from flask_restplus import Namespace, Resource\n'), (33, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'banned': '(False)', 'hidden': '(False)'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (43, 'flask.request.get_json', 'request.get_json', ([], {}), False, 'from flask import session, request, abort\n'), (44, 'CTFd.schemas.users.UserSchema', 'UserSchema', (['"""admin"""'], {}), False, 'from CTFd.schemas.users import UserSchema\n'), (50, 'CTFd.models.db.session.add', 'db.session.add', (['response.data'], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (51, 'CTFd.models.db.session.commit', 'db.session.commit', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (53, 'flask.request.args.get', 'request.args.get', (['"""notify"""'], {}), False, 'from flask import session, request, abort\n'), (57, 'CTFd.cache.clear_standings', 'clear_standings', ([], {}), False, 'from CTFd.cache import clear_standings\n'), (87, 'flask.request.get_json', 'request.get_json', ([], {}), False, 'from flask import session, request, abort\n'), (89, 'CTFd.schemas.users.UserSchema', 'UserSchema', ([], {'view': '"""admin"""', 'instance': 'user', 'partial': '(True)'}), False, 'from CTFd.schemas.users import UserSchema\n'), (94, 'CTFd.models.db.session.commit', 'db.session.commit', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (98, 'CTFd.models.db.session.close', 'db.session.close', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (100, 'CTFd.cache.clear_standings', 'clear_standings', ([], {}), False, 'from CTFd.cache import clear_standings\n'), (113, 'CTFd.models.db.session.commit', 'db.session.commit', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (114, 'CTFd.models.db.session.close', 'db.session.close', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (116, 'CTFd.cache.clear_standings', 'clear_standings', ([], {}), False, 'from CTFd.cache import clear_standings\n'), (125, 'CTFd.utils.user.get_current_user', 'get_current_user', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (133, 'CTFd.utils.user.get_current_user', 'get_current_user', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (134, 'flask.request.get_json', 'request.get_json', ([], {}), False, 'from flask import session, request, abort\n'), (135, 'CTFd.schemas.users.UserSchema', 'UserSchema', ([], {'view': '"""self"""', 'instance': 'user', 'partial': '(True)'}), False, 'from CTFd.schemas.users import UserSchema\n'), (140, 'CTFd.models.db.session.commit', 'db.session.commit', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (143, 'CTFd.models.db.session.close', 'db.session.close', ([], {}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (145, 'CTFd.cache.clear_standings', 'clear_standings', ([], {}), False, 'from CTFd.cache import clear_standings\n'), (154, 'CTFd.utils.user.get_current_user', 'get_current_user', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (170, 'CTFd.utils.user.get_current_user', 'get_current_user', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (178, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (192, 'CTFd.utils.user.get_current_user', 'get_current_user', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (244, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (72, 'flask.abort', 'abort', (['(404)'], {}), False, 'from flask import session, request, abort\n'), (213, 'flask.abort', 'abort', (['(404)'], {}), False, 'from flask import session, request, abort\n'), (236, 'flask.abort', 'abort', (['(404)'], {}), False, 'from flask import session, request, abort\n'), (263, 'flask.abort', 'abort', (['(404)'], {}), False, 'from flask import session, request, abort\n'), (34, 'CTFd.schemas.users.UserSchema', 'UserSchema', ([], {'view': '"""user"""', 'many': '(True)'}), False, 'from CTFd.schemas.users import UserSchema\n'), (69, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (71, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (86, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (106, 'CTFd.models.Notifications.query.filter_by', 'Notifications.query.filter_by', ([], {'user_id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (107, 'CTFd.models.Awards.query.filter_by', 'Awards.query.filter_by', ([], {'user_id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (108, 'CTFd.models.Unlocks.query.filter_by', 'Unlocks.query.filter_by', ([], {'user_id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (109, 'CTFd.models.Submissions.query.filter_by', 'Submissions.query.filter_by', ([], {'user_id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (110, 'CTFd.models.Solves.query.filter_by', 'Solves.query.filter_by', ([], {'user_id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (111, 'CTFd.models.Tracking.query.filter_by', 'Tracking.query.filter_by', ([], {'user_id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (112, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (157, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (158, 'CTFd.schemas.submissions.SubmissionSchema', 'SubmissionSchema', ([], {'view': 'view', 'many': '(True)'}), False, 'from CTFd.schemas.submissions import SubmissionSchema\n'), (173, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (174, 'CTFd.schemas.submissions.SubmissionSchema', 'SubmissionSchema', ([], {'view': 'view', 'many': '(True)'}), False, 'from CTFd.schemas.submissions import SubmissionSchema\n'), (195, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (196, 'CTFd.schemas.awards.AwardSchema', 'AwardSchema', ([], {'view': 'view', 'many': '(True)'}), False, 'from CTFd.schemas.awards import AwardSchema\n'), (210, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (212, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (215, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (217, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (218, 'CTFd.schemas.submissions.SubmissionSchema', 'SubmissionSchema', ([], {'view': 'view', 'many': '(True)'}), False, 'from CTFd.schemas.submissions import SubmissionSchema\n'), (233, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (235, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (237, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (239, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (240, 'CTFd.schemas.submissions.SubmissionSchema', 'SubmissionSchema', ([], {'view': 'view', 'many': '(True)'}), False, 'from CTFd.schemas.submissions import SubmissionSchema\n'), (260, 'CTFd.models.Users.query.filter_by', 'Users.query.filter_by', ([], {'id': 'user_id'}), False, 'from CTFd.models import db, Users, Solves, Awards, Tracking, Unlocks, Submissions, Notifications\n'), (262, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (264, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (266, 'CTFd.utils.user.is_admin', 'is_admin', ([], {}), False, 'from CTFd.utils.user import get_current_user, is_admin\n'), (267, 'CTFd.schemas.awards.AwardSchema', 'AwardSchema', ([], {'view': 'view', 'many': '(True)'}), False, 'from CTFd.schemas.awards import AwardSchema\n'), (126, 'CTFd.schemas.users.UserSchema', 'UserSchema', (['"""self"""'], {}), False, 'from CTFd.schemas.users import UserSchema\n'), (74, 'flask.session.get', 'session.get', (['"""type"""', '"""user"""'], {}), False, 'from flask import session, request, abort\n')]