import uuid

from django.conf import settings
from django.db import models
from django.utils import timezone


class Source(models.Model):
    name = models.CharField(max_length=120)
    url = models.CharField(max_length=500, blank=True, default="")
    is_default = models.BooleanField(default=False)

    class Meta:
        db_table = "sources"

    def __str__(self) -> str:
        return self.name


class Review(models.Model):
    slug = models.CharField(max_length=120)
    title = models.CharField(max_length=255)
    description = models.TextField(blank=True, default="")
    objective = models.TextField(blank=True, default="")
    population = models.CharField(max_length=200, blank=True, default="")
    intervention = models.CharField(max_length=200, blank=True, default="")
    comparison = models.CharField(max_length=200, blank=True, default="")
    outcome = models.CharField(max_length=200, blank=True, default="")
    context = models.CharField(max_length=200, blank=True, default="")
    quality_assessment_cutoff_score = models.FloatField(default=0.0)
    publish_status = models.CharField(max_length=1, default="U")
    synthesis_draft = models.TextField(blank=True, default="")
    is_public = models.BooleanField(default=False)
    is_demo = models.BooleanField(default=False)
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="owned_reviews",
        db_column="owner_id",
    )
    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(default=timezone.now)
    co_authors = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name="collaborations",
        blank=True,
        db_table="review_coauthors",
    )
    sources = models.ManyToManyField(
        Source,
        related_name="reviews",
        blank=True,
        db_table="review_sources",
    )

    class Meta:
        db_table = "reviews"
        constraints = [
            models.UniqueConstraint(
                fields=["owner", "slug"], name="uq_review_owner_slug"
            ),
        ]

    def is_author(self, user) -> bool:
        if user is None or not getattr(user, "is_authenticated", False):
            return False
        if self.owner_id == user.id:
            return True
        return self.co_authors.filter(pk=user.pk).exists()

    def is_main_author(self, user) -> bool:
        if user is None or not getattr(user, "is_authenticated", False):
            return False
        return self.owner_id == user.id

    def all_authors(self):
        return [self.owner] + list(self.co_authors.all())

    def get_base_search_session(self):
        return self.search_sessions.filter(source_id__isnull=True).first()

    def get_source_search_session(self, source_id: int):
        return self.search_sessions.filter(source_id=source_id).first()

    def ensure_search_sessions(self):
        from rsl.services.search_sessions import ensure_all_sessions

        ensure_all_sessions(self)

    def get_final_selection_articles(self):
        accepted = list(self.articles.filter(status="A"))
        if not self.quality_questions.exists() or self.quality_assessment_cutoff_score <= 0:
            return accepted
        cutoff = self.quality_assessment_cutoff_score
        selected = []
        for article in accepted:
            score = sum(
                a.answer.weight
                for a in article.quality_assessments.select_related("answer").all()
                if a.answer_id is not None and a.answer is not None
            )
            if score >= cutoff:
                selected.append(article)
        return selected

    def __str__(self) -> str:
        return self.slug


class Question(models.Model):
    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="questions")
    question = models.TextField()
    parent = models.ForeignKey(
        "self", null=True, blank=True, on_delete=models.CASCADE, related_name="subquestions"
    )
    order = models.IntegerField(default=0)

    class Meta:
        db_table = "questions"
        ordering = ["order"]


class Hypothesis(models.Model):
    TYPE_LABELS = {"S": "Forte", "W": "Fraca"}

    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="hypotheses")
    statement = models.TextField()
    hypothesis_type = models.CharField(max_length=1, default="S")
    order = models.IntegerField(default=0)

    class Meta:
        db_table = "hypotheses"
        ordering = ["order"]

    @property
    def type_label(self) -> str:
        return self.TYPE_LABELS.get(self.hypothesis_type, self.hypothesis_type)


class SelectionCriteria(models.Model):
    review = models.ForeignKey(
        Review, on_delete=models.CASCADE, related_name="selection_criteria"
    )
    criteria_type = models.CharField(max_length=1)
    description = models.TextField()

    class Meta:
        db_table = "selection_criteria"


class Keyword(models.Model):
    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="keywords")
    description = models.CharField(max_length=200)
    related_to = models.CharField(max_length=1, default="P")
    synonym_of = models.ForeignKey(
        "self", null=True, blank=True, on_delete=models.SET_NULL, related_name="synonyms"
    )

    class Meta:
        db_table = "keywords"


class SearchSession(models.Model):
    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="search_sessions")
    source = models.ForeignKey(Source, null=True, blank=True, on_delete=models.SET_NULL)
    search_string = models.TextField(blank=True, default="")
    version = models.IntegerField(default=1)

    class Meta:
        db_table = "search_sessions"
        constraints = [
            models.UniqueConstraint(
                fields=["review", "source"], name="uq_search_session_review_source"
            ),
        ]


class Article(models.Model):
    STATUS_LABELS = {
        "U": "Não classificado",
        "R": "Rejeitado",
        "A": "Aceito",
        "D": "Duplicado",
    }

    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="articles")
    source = models.ForeignKey(Source, null=True, blank=True, on_delete=models.SET_NULL)
    selection_criteria = models.ForeignKey(
        SelectionCriteria, null=True, blank=True, on_delete=models.SET_NULL
    )
    title = models.TextField(blank=True, default="")
    authors = models.TextField(blank=True, default="")
    journal = models.CharField(max_length=500, blank=True, default="")
    year = models.CharField(max_length=20, blank=True, default="")
    volume = models.CharField(max_length=50, blank=True, default="")
    number = models.CharField(max_length=50, blank=True, default="")
    pages = models.CharField(max_length=50, blank=True, default="")
    doi = models.CharField(max_length=255, blank=True, default="")
    abstract = models.TextField(blank=True, default="")
    bibtex_key = models.CharField(max_length=255, blank=True, default="")
    entry_type = models.CharField(max_length=50, blank=True, default="")
    publisher = models.CharField(max_length=255, blank=True, default="")
    url = models.CharField(max_length=500, blank=True, default="")
    affiliation = models.TextField(blank=True, default="")
    author_keywords = models.TextField(blank=True, default="")
    index_keywords = models.TextField(blank=True, default="")
    issn = models.CharField(max_length=50, blank=True, default="")
    language = models.CharField(max_length=50, blank=True, default="")
    note = models.TextField(blank=True, default="")
    pmid = models.CharField(max_length=32, blank=True, default="")
    status = models.CharField(max_length=1, default="U")
    comments = models.TextField(blank=True, default="")
    finished_data_extraction = models.BooleanField(default=False)
    thesis_highlight = models.BooleanField(default=False)
    thesis_tags = models.CharField(max_length=500, blank=True, default="")
    thesis_reminder = models.TextField(blank=True, default="")
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="articles_created",
    )
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="articles_updated",
    )
    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(default=timezone.now)

    class Meta:
        db_table = "articles"

    @property
    def status_label(self):
        return self.STATUS_LABELS.get(self.status, self.status)

    @property
    def thesis_tag_list(self) -> list[str]:
        if not self.thesis_tags:
            return []
        return [t.strip() for t in self.thesis_tags.split(",") if t.strip()]

    def quality_score(self) -> float:
        from rsl.services.quality_score import compute_article_quality_score

        return compute_article_quality_score(self)


class QualityQuestion(models.Model):
    review = models.ForeignKey(
        Review, on_delete=models.CASCADE, related_name="quality_questions"
    )
    description = models.TextField()
    order = models.IntegerField(default=0)

    class Meta:
        db_table = "quality_questions"
        ordering = ["order"]


class QualityAnswer(models.Model):
    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="quality_answers")
    description = models.CharField(max_length=120)
    weight = models.FloatField(default=0.0)
    order = models.IntegerField(default=0)

    class Meta:
        db_table = "quality_answers"
        ordering = ["order"]


class QualityAssessment(models.Model):
    article = models.ForeignKey(
        Article, on_delete=models.CASCADE, related_name="quality_assessments"
    )
    question = models.ForeignKey(
        QualityQuestion, on_delete=models.CASCADE, related_name="assessments"
    )
    answer = models.ForeignKey(
        QualityAnswer, null=True, blank=True, on_delete=models.SET_NULL
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL
    )

    class Meta:
        db_table = "quality_assessments"
        constraints = [
            models.UniqueConstraint(
                fields=["article", "question"], name="uq_qa_article_question"
            ),
        ]


class DataExtractionField(models.Model):
    FIELD_TYPES = {
        "B": "Sim/Não",
        "S": "Seleção múltipla",
        "F": "Decimal",
        "I": "Inteiro",
        "D": "Data",
        "O": "Texto",
        "M": "Texto longo",
    }

    review = models.ForeignKey(
        Review, on_delete=models.CASCADE, related_name="extraction_fields"
    )
    description = models.TextField()
    field_type = models.CharField(max_length=1, default="O")
    order = models.IntegerField(default=0)

    class Meta:
        db_table = "data_extraction_fields"
        ordering = ["order"]


class DataExtractionLookup(models.Model):
    field = models.ForeignKey(
        DataExtractionField, on_delete=models.CASCADE, related_name="lookups"
    )
    value = models.CharField(max_length=255)

    class Meta:
        db_table = "data_extraction_lookups"


class DataExtraction(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name="extractions")
    field = models.ForeignKey(
        DataExtractionField, on_delete=models.CASCADE, related_name="extractions"
    )
    value = models.TextField(blank=True, default="")
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL
    )
    select_values = models.ManyToManyField(
        DataExtractionLookup,
        blank=True,
        through="DataExtractionSelectValue",
        related_name="extractions",
    )

    class Meta:
        db_table = "data_extractions"
        constraints = [
            models.UniqueConstraint(
                fields=["article", "field"], name="uq_extraction_article_field"
            ),
        ]


class DataExtractionSelectValue(models.Model):
    """Through table; db_column matches existing MySQL schema."""

    extraction = models.ForeignKey(
        DataExtraction,
        on_delete=models.CASCADE,
        db_column="dataextraction_id",
    )
    lookup = models.ForeignKey(
        DataExtractionLookup,
        on_delete=models.CASCADE,
        db_column="dataextractionlookup_id",
    )

    class Meta:
        db_table = "extraction_select_values"
        managed = False
        unique_together = [("extraction", "lookup")]


class Invite(models.Model):
    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="invites")
    invited_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="invites_sent",
    )
    invitee = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="invites_received",
    )
    invitee_email = models.EmailField(max_length=255)
    status = models.CharField(max_length=20, default="pending")
    code = models.CharField(max_length=36, unique=True, default=uuid.uuid4)
    created_at = models.DateTimeField(default=timezone.now)
    responded_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        db_table = "invites"
