from re import T
import uuid

from django.db import models
from django.utils import timezone
from authenticate.models import User
from institute.models import Institute, InstituteStudent
from question.models import Question
from wikiazma.storage_helper import public_storage


REPORT_CARD_STRATEGY_INSTANTLY = 'instantly'
REPORT_CARD_STRATEGY_ON_DATE_TIME = 'on_date_time'
REPORT_CARD_STRATEGY_ON_LAST_STUDENT = 'on_last_student'
REPORT_CARD_STRATEGY_MANUAL = 'manual'
ALLOWED_REPORT_CARD_STRATEGIES = [REPORT_CARD_STRATEGY_INSTANTLY, REPORT_CARD_STRATEGY_ON_DATE_TIME, REPORT_CARD_STRATEGY_ON_LAST_STUDENT,
                                  REPORT_CARD_STRATEGY_MANUAL]

ALLOWED_ANSWER_TEXT_FORMAT = ['markdownV1', 'htmlV1', 'jsonV1', 'text']


class ExamTemplateAbstract(models.Model):
    class Meta:
        abstract = True

    id = models.UUIDField(primary_key=True, unique=True,
                          db_index=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100, db_index=True)
    # JSONField uses jsonb by default
    rules = models.JSONField(null=True, blank=True)
    one_question_at_a_time = models.BooleanField(db_index=True, default=True)
    reanswer = models.BooleanField(db_index=True, default=True)
    report_card_strategy = models.CharField(
        max_length=20, db_index=True, default=REPORT_CARD_STRATEGY_MANUAL)
    shuffle_questions = models.BooleanField(db_index=True, default=True)
    shuffle_answers = models.BooleanField(db_index=True, default=True)
    apply_negative_scores = models.BooleanField(db_index=True)
    examiner_info_visibility = models.BooleanField(db_index=True, default=True)
    override_institute_name = models.CharField(
        max_length=100, db_index=True, null=True, blank=True)
    override_institute_logo = models.ImageField(
        upload_to='exam_template/override_institute_logo', storage=public_storage, null=True, blank=True)
    poster = models.ImageField(
        upload_to='exam/posters', null=True, blank=True, storage=public_storage)
    poster_thumbnail = models.ImageField(
        upload_to='exam/posters_thumbnails', null=True, blank=True, storage=public_storage)
    theme = models.CharField(max_length=20)
    delayable = models.BooleanField()
    price = models.IntegerField(null=True, blank=True)
    description = models.TextField(max_length=3000, null=True, blank=True)
    duration = models.DurationField(db_index=True, null=True, blank=True)
    max_delay = models.DurationField(null=True, blank=True)
    questions_count = models.IntegerField(db_index=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)
    finish_message = models.TextField(max_length=1000, null=True, blank=True)


class ExamTemplate(ExamTemplateAbstract):
    institute = models.ForeignKey(
        Institute, on_delete=models.PROTECT, db_index=True)
    deleted = models.BooleanField(default=False, db_index=True)


class PublicExamTemplate(ExamTemplateAbstract):
    priority = models.IntegerField(db_index=True)


ENTRANCE_TYPE_PUBLIC = 'public'
ENTRANCE_TYPE_API_INVITE = 'API_invite'
ENTRANCE_TYPE_ADMIN_INVITE = 'admin_invite'
ALLOWED_ENTRANCE_TYPES = [ENTRANCE_TYPE_PUBLIC,
                          ENTRANCE_TYPE_API_INVITE, ENTRANCE_TYPE_ADMIN_INVITE]


class Exam(models.Model):
    id = models.UUIDField(primary_key=True, unique=True,
                          db_index=True, default=uuid.uuid4, editable=False)
    institute = models.ForeignKey(
        Institute, on_delete=models.PROTECT, db_index=True)
    # JSONField uses jsonb by default
    rules = models.JSONField(null=True, blank=True)
    public = models.BooleanField(db_index=True, default=False)
    public_id = models.CharField(
        max_length=100, db_index=True, null=True, blank=True)
    name = models.CharField(max_length=100, db_index=True)
    one_question_at_a_time = models.BooleanField(db_index=True, default=True)
    reanswer = models.BooleanField(db_index=True, default=True)
    report_card_strategy = models.CharField(
        max_length=20, db_index=True, default=REPORT_CARD_STRATEGY_MANUAL)
    report_card_date = models.DateTimeField(
        db_index=True, null=True, blank=True)
    theme = models.CharField(max_length=20)
    shuffle_questions = models.BooleanField(db_index=True, default=True)
    shuffle_answers = models.BooleanField(db_index=True, default=True)
    apply_negative_scores = models.BooleanField(db_index=True)
    examiner_info_visibility = models.BooleanField(db_index=True, default=True)
    override_institute_name = models.CharField(
        max_length=100, db_index=True, null=True, blank=True)
    override_institute_logo = models.ImageField(
        upload_to='exam/override_institute_logo', storage=public_storage, null=True, blank=True)
    poster = models.ImageField(
        upload_to='exam/posters', null=True, blank=True, storage=public_storage)
    poster_thumbnail = models.ImageField(
        upload_to='exam/posters_thumbnails', null=True, blank=True, storage=public_storage)
    # JSONField uses jsonb by default
    generated_exam = models.JSONField(null=True, blank=True)
    description = models.TextField(max_length=3000, null=True, blank=True)
    delayable = models.BooleanField()
    start_date = models.DateTimeField(db_index=True, null=True, blank=True)
    end_date = models.DateTimeField(db_index=True, null=True, blank=True)
    duration = models.DurationField(db_index=True, null=True, blank=True)
    max_delay = models.DurationField(null=True, blank=True)
    deleted = models.BooleanField(default=False, db_index=True)
    questions_count = models.IntegerField(db_index=True)
    price = models.PositiveIntegerField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)
    finish_message = models.TextField(max_length=1000, null=True, blank=True)
    paused = models.BooleanField(default=False)


EXAM_PAGE_STATE_NOT_GENERATED = 'not_generated'
EXAM_PAGE_STATE_NOT_ATTENDED = 'not_attended'
EXAM_PAGE_STATE_IN_PROGRESS = 'in_progress'
EXAM_PAGE_STATE_FINISHED = 'finished'
EXAM_PAGE_STATE_SCORES_PUBLISHED = 'scores_published'


class ExamPage(models.Model):
    id = models.UUIDField(primary_key=True, unique=True,
                          db_index=True, default=uuid.uuid4, editable=False)
    institute = models.ForeignKey(
        Institute, on_delete=models.PROTECT, db_index=True)
    entrance_token = models.CharField(
        max_length=60, db_index=True, unique=True, null=True, blank=True)
    entrance_type = models.CharField(max_length=20, db_index=True)
    device_token = models.CharField(
        max_length=60, db_index=True, unique=True, null=True, blank=True)
    device_history = models.JSONField(default=list, blank=True)
    entrance_ip = models.CharField(max_length=60, db_index=True, blank=True)
    user = models.ForeignKey(
        User, on_delete=models.SET_NULL, db_index=True, null=True, blank=True)
    institute_student = models.ForeignKey(
        InstituteStudent, on_delete=models.SET_NULL, db_index=True, null=True, blank=True)
    exam = models.ForeignKey(Exam, on_delete=models.PROTECT, db_index=True)
    # each exam page has its own reanswer and one_question_at_a_time
    # it is because the exam can be edited after the exam_page was created
    # and it can break the exam_page if for example we change its reanswer from True to False
    one_question_at_a_time = models.BooleanField(db_index=True, default=True)
    reanswer = models.BooleanField(db_index=True, default=True)
    apply_negative_scores = models.BooleanField(db_index=True)
    generated_exam = models.JSONField(null=True, blank=True)
    first_available_question_index = models.IntegerField()
    first_available_group_index = models.IntegerField()
    user_inputs = models.JSONField(default=list, blank=True)
    user_inputs_history = models.JSONField(default=list, blank=True)
    final_score = models.CharField(max_length=10, null=True, blank=True)
    state = models.CharField(max_length=20, db_index=True,
                             default=EXAM_PAGE_STATE_NOT_ATTENDED)
    state_history = models.JSONField(default=list, blank=True)
    delayable = models.BooleanField()
    entrance_date = models.DateTimeField(db_index=True, null=True)
    start_date = models.DateTimeField(db_index=True, null=True, blank=True)
    end_date = models.DateTimeField(db_index=True, null=True, blank=True)
    duration = models.DurationField(db_index=True, null=True, blank=True)
    max_delay = models.DurationField(null=True, blank=True)
    questions_count = models.IntegerField(db_index=True, null=True, blank=True)
    report_card = models.JSONField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)
    finished_at = models.DateTimeField(null=True, blank=True)

    @property
    def invalid_in_progress_state(self) -> bool:
        state_condition = self.state == EXAM_PAGE_STATE_IN_PROGRESS
        end_date_consotion = self.end_date and self.end_date < timezone.now()
        duration_consition = self.duration and self.entrance_date and self.duration < (
            timezone.now() - self.entrance_date)

        return state_condition and (end_date_consotion or duration_consition)


class Corrections(models.Model):
    id = models.UUIDField(primary_key=True, unique=True,
                          db_index=True, default=uuid.uuid4, editable=False)
    corrector = models.ForeignKey(
        User, on_delete=models.SET_NULL, db_index=True, null=True)
    exam_page = models.ForeignKey(
        ExamPage, on_delete=models.PROTECT, db_index=True)
    question = models.ForeignKey(
        Question, on_delete=models.SET_NULL, db_index=True, null=True)
    group_index = models.IntegerField()
    question_index = models.IntegerField()
    blank = models.BooleanField(db_index=True)
    score = models.FloatField(null=True, blank=True)
    correct_choices = models.IntegerField(null=True, blank=True)
    wrong_choices = models.IntegerField(null=True, blank=True)
    blank_choices = models.IntegerField(null=True, blank=True)
    comment_for_student = models.TextField(
        max_length=3000, null=True, blank=True)
    comment_for_other_correctors = models.TextField(
        max_length=3000, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)
    #TODO: psycopg2.errors.DuplicateTable
    # class Meta:
    #     index_together = [['exam_page', 'group_index', 'question_index']]
    #     unique_together = [['exam_page', 'group_index', 'question_index']]


class AnswerFile(models.Model):
    class Meta:
        # Our queries are like
        # SELECT * FROM AnswerFile WHERE exam_page='...' AND group_index=... AND question_index=...
        # or
        # SELECT * FROM AnswerFile WHERE exam_page='...' AND group_index=... AND question_index=... AND !(submitted==True AND deleted=True)
        # The second query will find at most 10 objects using our composite index and then
        # does a sequential scan on these 10 objects to remove objects that does not match "!(submitted==True AND deleted=True)"
        # ORDER BY priority does not an index too because a sequential ordering of 10 objects is cheaper than accessing index page files
        index_together = [['exam_page', 'group_index', 'question_index']]
        # If the index grows too large, we can remove group_index from it.
        # There will be at most 20 groups given an exam_page and question_index.
        # If we remove question_index, there will be at most 100 questions given an exam_page and group_index.

    id = models.UUIDField(primary_key=True, unique=True,
                          db_index=True, default=uuid.uuid4, editable=False)
    exam_page = models.ForeignKey(ExamPage, on_delete=models.CASCADE)
    question_index = models.IntegerField()
    group_index = models.IntegerField()
    # an index on this field will probably never be used by postgres considering our queries
    file_type = models.CharField(max_length=20)
    file = models.FileField(upload_to='answer_files', storage=public_storage)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)
