# Standard Library
from datetime import datetime
from unittest.mock import patch

# Third-Party Imports
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework.test import APIClient

# App Imports
from api.tests import APIBaseTestCase
from core.models import AssetIncidentReport

client = APIClient()


class AssetIncidentReportAPITest(APIBaseTestCase):
    """ Tests for the AssetIncidentReport API """

    def test_non_authenticated_user_view_incident_report(self):
        response = client.get(self.incident_report_url)
        self.assertEqual(
            response.data, {"detail": "Authentication credentials were not provided."}
        )

    @patch("api.authentication.auth.verify_id_token")
    def test_authenticated_user_post_incident_report(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        data = {
            "asset": self.asset.id,
            "incident_type": "Loss",
            "incident_location": "CDB",
            "incident_description": "Lorem Ipsum",
            "injuries_sustained": "N/a",
            "loss_of_property": "Mobile Phone",
            "witnesses": "John Doe +2347548458457",
            "police_abstract_obtained": "Yes",
        }
        response = client.post(
            f"{self.incident_report_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertIn(
            f"{self.asset.serial_number} - {self.asset.asset_code}",
            response.data.values(),
        )
        self.assertIn("submitted_by", response.data.keys())
        self.assertEqual(response.data["submitted_by"], self.user.email)
        self.assertEqual(response.status_code, 201)

    @patch("api.authentication.auth.verify_id_token")
    def test_authenticated_user_post_invalid_incident_type(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        data = {
            "asset": self.asset.id,
            "incident_type": "Invalid",
            "incident_location": "CDB",
            "incident_description": "Lorem Ipsum",
            "injuries_sustained": "N/a",
            "loss_of_property": "Mobile Phone",
            "witnesses": "John Doe +2347548458457",
            "police_abstract_obtained": "Yes",
        }
        response = client.post(
            f"{self.incident_report_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(
            response.data, {"incident_type": ['"Invalid" is not a valid choice.']}
        )
        self.assertEqual(response.status_code, 400)

    @patch("api.authentication.auth.verify_id_token")
    def test_authenticated_user_post_empty_incident_fields(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        data = {
            "asset": self.asset.id,
            "incident_type": "Loss",
            "incident_location": "",
            "incident_description": "Lorem",
            "police_abstract_obtained": "Yes",
        }
        response = client.post(
            f"{self.incident_report_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(
            response.data, {"incident_location": ["This field may not be blank."]}
        )
        self.assertEqual(response.status_code, 400)

    @patch("api.authentication.auth.verify_id_token")
    def test_authenticated_user_get_incident_report(self, mock_verify_id_token):
        incident = AssetIncidentReport(
            asset=self.asset,
            incident_type="Damaged",
            incident_location="44",
            incident_description="Mugging",
            injuries_sustained="Black eye",
            loss_of_property="Laptop",
            witnesses="John Doe",
            police_abstract_obtained="Yes",
            created_at=datetime(2019, 4, 13),
        )
        incident.save()
        mock_verify_id_token.return_value = {"email": self.user.email}
        response = client.get(
            f"{self.incident_report_url}",
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(
            len(response.data["results"]), AssetIncidentReport.objects.count()
        )
        date = incident.created_at
        date = f"{date.year}-{date.month}-{date.day} {date.hour}:{date.minute}"
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["results"][0]["created_at"], date)
        self.assertEqual(
            response.data["results"][0]["incident_type"], incident.incident_type
        )
        self.assertEqual(
            response.data["results"][0]["incident_location"], incident.incident_location
        )
        self.assertEqual(
            response.data["results"][0]["incident_description"],
            incident.incident_description,
        )
        self.assertEqual(
            response.data["results"][0]["injuries_sustained"],
            incident.injuries_sustained,
        )
        self.assertEqual(
            response.data["results"][0]["loss_of_property"], incident.loss_of_property
        )
        self.assertEqual(response.data["results"][0]["witnesses"], incident.witnesses)
        self.assertEqual(
            response.data["results"][0]["police_abstract_obtained"],
            incident.police_abstract_obtained,
        )
        self.assertTrue(
            response.data["results"][1]["created_at"]
            > response.data["results"][0]["created_at"]
        )
        self.assertTrue(
            response.data["results"][0]["id"] > response.data["results"][1]["id"]
        )

    @patch("api.authentication.auth.verify_id_token")
    def test_authenticated_user_get_single_incident_report(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        response = client.get(
            f"{self.incident_report_url}/{self.incident_report.id}/",
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertIn(self.incident_report.id, response.data.values())
        self.assertEqual(response.status_code, 200)
        date = self.incident_report.created_at
        date = f"{date.year}-{date.month}-{date.day} {date.hour}:{date.minute}"
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["created_at"], date)
        self.assertEqual(
            response.data["incident_type"], self.incident_report.incident_type
        )
        self.assertEqual(
            response.data["incident_location"], self.incident_report.incident_location
        )
        self.assertEqual(
            response.data["incident_description"],
            self.incident_report.incident_description,
        )
        self.assertEqual(
            response.data["injuries_sustained"], self.incident_report.injuries_sustained
        )
        self.assertEqual(
            response.data["loss_of_property"], self.incident_report.loss_of_property
        )
        self.assertEqual(response.data["witnesses"], self.incident_report.witnesses)
        self.assertEqual(
            response.data["police_abstract_obtained"],
            self.incident_report.police_abstract_obtained,
        )

    @patch("api.authentication.auth.verify_id_token")
    def test_cant_allow_put_incident_report(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        response = client.put(
            f"{self.incident_report_url}/{self.incident_report.id}/",
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(response.data, {"detail": 'Method "PUT" not allowed.'})
        self.assertEqual(response.status_code, 405)

    @patch("api.authentication.auth.verify_id_token")
    def test_cant_allow_patch_incident_report(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        response = client.patch(
            f"{self.incident_report_url}",
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(response.data, {"detail": 'Method "PATCH" not allowed.'})
        self.assertEqual(response.status_code, 405)

    @patch("api.authentication.auth.verify_id_token")
    def test_cant_allow_delete_incident_report(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.user.email}
        response = client.delete(
            f"{self.incident_report_url}",
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(response.data, {"detail": 'Method "DELETE" not allowed.'})
        self.assertEqual(response.status_code, 405)

    @patch("api.authentication.auth.verify_id_token")
    def test_can_allow_patch_of_incident_report_status(self, mock_verify_id_token):
        mock_verify_id_token.return_value = {"email": self.admin_user.email}
        data = {
            "incident_report_state": "internal assessment",
            "asset_state_from_report": "Damaged",
        }
        response = client.patch(
            f"{self.incident_report_status_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        data["id"] = self.report_status.id
        data["asset_incident_report"] = self.report_status.asset_incident_report.id
        self.assertEqual(response.data, data)
        self.assertEqual(response.status_code, 200)

    @patch("api.authentication.auth.verify_id_token")
    def test_require_both_fields_to_patch_incident_report_status(
        self, mock_verify_id_token
    ):
        mock_verify_id_token.return_value = {"email": self.admin_user.email}
        data = {
            "asset_incident_report": self.report_status.asset_incident_report.id,
            "incident_report_state": "internal assessment",
        }
        response = client.patch(
            f"{self.incident_report_status_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(
            response.data,
            {
                "Error": "Ensure that incident_report_state and asset_state_from_report fields are filled"
            },
        )
        self.assertEqual(response.status_code, 400)

    @patch("api.authentication.auth.verify_id_token")
    def test_restrict_patch_of_incident_report_status_for_external_assessment(
        self, mock_verify_id_token
    ):
        mock_verify_id_token.return_value = {"email": self.admin_user.email}
        data = {
            "incident_report_state": "external assessment",
            "asset_state_from_report": "requires external assessment",
        }
        response = client.patch(
            f"{self.incident_report_status_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertEqual(
            response.data,
            {"Error": "Asset state option is not valid for given report state"},
        )
        self.assertEqual(response.status_code, 400)

    @patch("api.authentication.auth.verify_id_token")
    def test_authenticated_user_post_police_abstract_incident_report(
        self, mock_verify_id_token
    ):
        mock_verify_id_token.return_value = {"email": self.user.email}
        police_abstract = SimpleUploadedFile(
            "file.mp4", b"file_content", content_type="video/mp4"
        )
        data = {
            "asset": self.asset.id,
            "incident_type": "Loss",
            "incident_location": "CDB",
            "incident_description": "Lorem Ipsum",
            "injuries_sustained": "N/a",
            "loss_of_property": "Mobile Phone",
            "witnesses": "John Doe +2347548458457",
            "police_abstract_obtained": "Yes",
            "police_abstract": police_abstract,
        }
        response = client.post(
            f"{self.incident_report_url}",
            data,
            HTTP_AUTHORIZATION="Token {}".format(self.token_user),
        )
        self.assertIn(
            f"{self.asset.serial_number} - {self.asset.asset_code}",
            response.data.values(),
        )
        self.assertTrue("police_abstract" in response.data)
        self.assertTrue("submitted_by" in response.data)
        self.assertEqual(response.data["submitted_by"], self.user.email)
        self.assertEqual(response.status_code, 201)
