#!/usr/bin/env python
# -*- coding: utf-8 -*-

from CTFd.models import Discards, Fails, Solves
from tests.helpers import (
    create_ctfd,
    destroy_ctfd,
    gen_challenge,
    gen_fail,
    gen_solve,
    gen_team,
    login_as_user,
    register_user,
)


def test_api_submissions_get_non_admin():
    app = create_ctfd()
    with app.app_context():
        gen_challenge(app.db)
        gen_solve(app.db, user_id=1)
        with app.test_client() as client:
            # test_api_submissions_get_non_admin
            """Can a user get /api/v1/submissions if not admin"""
            r = client.get("/api/v1/submissions", json="")
            assert r.status_code == 403

            # test_api_submissions_post_non_admin
            """Can a user post /api/v1/submissions if not admin"""
            r = client.post("/api/v1/submissions")
            assert r.status_code == 403

            # test_api_submission_get_non_admin
            """Can a user get /api/v1/submissions/<submission_id> if not admin"""
            r = client.get("/api/v1/submissions/1", json="")
            assert r.status_code == 403

            # test_api_submission_delete_non_admin
            """Can a user delete /api/v1/submissions/<submission_id> if not admin"""
            r = client.delete("/api/v1/submissions/1", json="")
            assert r.status_code == 403
    destroy_ctfd(app)


def test_api_submissions_get_admin():
    """Can a user get /api/v1/submissions if admin"""
    app = create_ctfd()
    with app.app_context():
        with login_as_user(app, "admin") as client:
            r = client.get("/api/v1/submissions", json="")
            assert r.status_code == 200
            r = client.get("/api/v1/submissions?user_id=1", json="")
            assert r.status_code == 200
    destroy_ctfd(app)


def test_api_submissions_post_admin():
    """Can a user post /api/v1/submissions if admin"""
    app = create_ctfd()
    with app.app_context():
        gen_challenge(app.db)
        with login_as_user(app, name="admin") as client:
            r = client.post(
                "/api/v1/submissions",
                json={
                    "provided": "MARKED AS SOLVED BY ADMIN",
                    "user_id": 1,
                    "team_id": None,
                    "challenge_id": "1",
                    "type": "correct",
                },
            )
            assert r.status_code == 200
    destroy_ctfd(app)


def test_api_submission_get_admin():
    """Can a user get /api/v1/submissions/<submission_id> if admin"""
    app = create_ctfd()
    with app.app_context():
        gen_solve(app.db, user_id=1)
        with login_as_user(app, "admin") as client:
            r = client.get("/api/v1/submissions/1", json="")
            assert r.status_code == 200
    destroy_ctfd(app)


def test_api_submission_delete_admin():
    """Can a user patch /api/v1/submissions/<submission_id> if admin"""
    app = create_ctfd()
    with app.app_context():
        gen_solve(app.db, user_id=1)
        with login_as_user(app, "admin") as client:
            r = client.delete("/api/v1/submissions/1", json="")
            assert r.status_code == 200
            assert r.get_json().get("data") is None
    destroy_ctfd(app)


def test_api_submission_patch_correct():
    """Test that patching a submission to correct creates a solve"""
    app = create_ctfd()
    with app.app_context():
        register_user(app)
        gen_challenge(app.db)
        gen_fail(app.db, challenge_id=1, user_id=2)
        assert Solves.query.count() == 0
        with login_as_user(app, "admin") as client:
            r = client.patch("/api/v1/submissions/1", json={"type": "correct"})
            assert r.status_code == 200
            assert Fails.query.count() == 0
            assert Solves.query.count() == 1
            assert Discards.query.count() == 1
    destroy_ctfd(app)


def test_api_submission_patch_correct_with_existing_solve_fails():
    """Test that patching a submission to correct fails if a solve already exists"""
    app = create_ctfd()
    with app.app_context():
        register_user(app)
        gen_challenge(app.db)
        gen_fail(app.db, challenge_id=1, user_id=2)
        gen_solve(app.db, challenge_id=1, user_id=2)
        assert Solves.query.count() == 1
        with login_as_user(app, "admin") as client:
            r = client.patch("/api/v1/submissions/1", json={"type": "correct"})
            data = r.get_json()
            assert r.status_code == 400
            assert data["success"] is False
            assert Solves.query.count() == 1
            assert Fails.query.count() == 1
            assert Discards.query.count() == 0
    destroy_ctfd(app)


def test_api_submission_patch_correct_scoreboard():
    "If we adjust a submission for someone the scoreboard should be correct accounting for the time of the adjusted submission"
    app = create_ctfd()
    with app.app_context():
        # Create 2 test users
        register_user(app, name="user1", email="user1@examplectf.com")
        register_user(app, name="user2", email="user2@examplectf.com")

        # Create 2 test challenges
        gen_challenge(app.db, name="chal1")
        gen_challenge(app.db, name="chal2")

        # Give the first test user only fails
        gen_fail(app.db, challenge_id=1, user_id=2)
        gen_fail(app.db, challenge_id=2, user_id=2)
        # Give the second test user only solves
        gen_solve(app.db, challenge_id=1, user_id=3)
        gen_solve(app.db, challenge_id=2, user_id=3)
        with login_as_user(app, "admin") as client:
            # user2 who has both solves should be considered on top
            scoreboard = client.get("/api/v1/scoreboard").get_json()["data"]
            assert len(scoreboard) == 1
            assert scoreboard[0]["name"] == "user2"

            # We mark user1's first solve as correct
            # This should give them 100 points
            # It should not place them above user2 who has 200 points
            client.patch("/api/v1/submissions/1", json={"type": "correct"})
            scoreboard = client.get("/api/v1/scoreboard").get_json()["data"]
            assert len(scoreboard) == 2
            assert scoreboard[0]["name"] == "user2"
            assert scoreboard[1]["name"] == "user1"
            assert scoreboard[1]["score"] == 100

            # We mark user1's second solve as correct
            # This should give them 200 points
            # It should place them above user2 who has 200 points but was not the first to solve the challenge
            # Based on time user1's attempts should be considered correct and first
            client.patch("/api/v1/submissions/2", json={"type": "correct"})
            scoreboard = client.get("/api/v1/scoreboard").get_json()["data"]
            assert len(scoreboard) == 2
            assert scoreboard[0]["name"] == "user1"
            assert scoreboard[0]["score"] == 200
            assert scoreboard[1]["name"] == "user2"
            assert scoreboard[1]["score"] == 200
    destroy_ctfd(app)


def test_api_submission_patch_correct_scoreboard_teams():
    "If we adjust a submission for a team the scoreboard should be correct after the adjustment"
    app = create_ctfd(user_mode="teams")
    with app.app_context():
        # Create 2 test teams each with only 1 user
        gen_team(app.db, name="team1", email="team1@examplectf.com", member_count=1)
        gen_team(app.db, name="team2", email="team2@examplectf.com", member_count=1)

        # Create 2 test challenges
        gen_challenge(app.db, name="chal1")
        gen_challenge(app.db, name="chal2")

        # Assign only fails to the user in team 1
        gen_fail(app.db, challenge_id=1, user_id=2, team_id=1)
        gen_fail(app.db, challenge_id=2, user_id=2, team_id=1)
        # Assign only solves to the user in team 2
        gen_solve(app.db, challenge_id=1, user_id=3, team_id=2)
        gen_solve(app.db, challenge_id=2, user_id=3, team_id=2)

        with login_as_user(app, "admin") as client:
            # team2 who has both solves should be considered on top
            scoreboard = client.get("/api/v1/scoreboard").get_json()["data"]
            assert len(scoreboard) == 1
            assert scoreboard[0]["name"] == "team2"

            # We then convert the first submission which should give team1 100 points
            # team1 should also now appear on the scoreboard in 2nd place
            client.patch("/api/v1/submissions/1", json={"type": "correct"})
            scoreboard = client.get("/api/v1/scoreboard").get_json()["data"]
            assert len(scoreboard) == 2
            assert scoreboard[0]["name"] == "team2"
            assert scoreboard[1]["name"] == "team1"
            assert scoreboard[1]["score"] == 100

            # We mark team1's second solve as correct
            # This should give them 200 points
            # It should place them above team2 who has 200 points but was not the first to solve the challenge
            # Based on time team1's attempts should be considered correct and first
            client.patch("/api/v1/submissions/2", json={"type": "correct"})
            scoreboard = client.get("/api/v1/scoreboard").get_json()["data"]
            assert len(scoreboard) == 2
            assert scoreboard[0]["name"] == "team1"
            assert scoreboard[0]["score"] == 200
            assert scoreboard[1]["name"] == "team2"
            assert scoreboard[1]["score"] == 200
    destroy_ctfd(app)
