from uuid import uuid4

from clinicedc_constants import CLOSED, RESOLVED
from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models.deletion import PROTECT
from django.template.loader import render_to_string
from django.urls.base import reverse
from django.utils import timezone

from edc_action_item.models.action_model_mixin import ActionModelMixin
from edc_dashboard.url_names import InvalidDashboardUrlName, url_names
from edc_model.models import BaseUuidModel
from edc_sites.managers import CurrentSiteManager
from edc_sites.model_mixins import SiteModelMixin
from edc_visit_tracking.utils import get_related_visit_model_cls

from ..action_items import DATA_QUERY_ACTION
from ..constants import AUTO_RESOLVED
from .data_dictionary import DataDictionary
from .model_mixins import DataQueryModelMixin
from .query_subject import QuerySubject
from .query_visit_schedule import QueryVisitSchedule
from .requisition_panel import RequisitionPanel


class DataQuery(DataQueryModelMixin, ActionModelMixin, SiteModelMixin, BaseUuidModel):
    action_name = DATA_QUERY_ACTION

    subject_identifier = models.CharField(max_length=50, default="", editable=False)

    registered_subject = models.ForeignKey(
        QuerySubject,
        verbose_name="Subject Identifier",
        on_delete=PROTECT,
        null=True,
        blank=False,
    )

    visit_schedule = models.ForeignKey(
        QueryVisitSchedule,
        verbose_name="Visit",
        on_delete=PROTECT,
        null=True,
        blank=True,
    )

    visit_code_sequence = models.IntegerField(
        verbose_name="Visit code sequence",
        validators=[MinValueValidator(0), MaxValueValidator(25)],
        null=True,
        blank=True,
    )

    timepoint = models.DecimalField(null=True, decimal_places=1, max_digits=6)

    data_dictionaries = models.ManyToManyField(
        DataDictionary,
        verbose_name="CRF question(s)",
        blank=True,
        help_text="select all that apply",
    )

    requisition_panel = models.ForeignKey(
        RequisitionPanel,
        verbose_name="Are responses linked to a requisition? If so, which",
        related_name="+",
        on_delete=PROTECT,
        null=True,
        blank=True,
        help_text="Requisition will be expected on day of visit.",
    )

    missed_visit = models.BooleanField(
        verbose_name="Visit reported as missed",
        default=False,
        help_text="If visit/timepoint was missed, data is not expected",
    )

    rule_generated = models.BooleanField(
        default=False, help_text="This query was auto-generated by a query rule."
    )

    rule_reference = models.CharField(
        verbose_name="Query rule reference", max_length=150, default=uuid4
    )

    objects = models.Manager()

    on_site = CurrentSiteManager()

    def __str__(self):
        return f"{self.action_identifier[-9:]}, {self.status}"

    def save(self, *args, **kwargs):
        self.subject_identifier = self.registered_subject.subject_identifier
        self.try_to_resolve_auto_generated_query()
        super().save(*args, **kwargs)

    def try_to_resolve_auto_generated_query(self):
        if (
            not self.dm_resolved
            and self.rule_generated
            and self.site_response_status == RESOLVED
            and self.site_response_text
            and self.site_response_text.strip() == AUTO_RESOLVED
        ):
            self.status = CLOSED
            self.resolved_datetime = timezone.now()
            self.dm_user = self.sender

    def form_and_numbers_to_string(self):
        ret = []
        model_verbose_names = [
            o.model_verbose_name for o in self.data_dictionaries.all().order_by("model")
        ]
        model_verbose_names = list(set(model_verbose_names))
        for model_verbose_name in model_verbose_names:
            numbers = [
                str(o.number)
                for o in self.data_dictionaries.filter(
                    model_verbose_name=model_verbose_name
                ).order_by("number")
            ]
            numbers = ", ".join(numbers)
            ret.append((model_verbose_name, numbers))
        return ret

    def get_action_item_display_name(self):
        pass

    def get_action_item_reason(self):
        try:
            url = url_names.get("subject_dashboard_url")
        except InvalidDashboardUrlName:
            visit_href = "#"
        else:
            try:
                visit = get_related_visit_model_cls().objects.get(
                    subject_identifier=self.registered_subject.subject_identifier,
                    visit_schedule_name=self.visit_schedule.visit_schedule_name,
                    schedule_name=self.visit_schedule.schedule_name,
                    visit_code=self.visit_schedule.visit_code,
                    visit_code_sequence=self.visit_code_sequence,
                )
            except ObjectDoesNotExist:
                visit_href = "#"
            except AttributeError as e:
                if (
                    "visit_schedule_name" not in str(e)
                    and "schedule_name" not in str(e)
                    and "visit_code" not in str(e)
                ):
                    raise
                visit_href = "#"
            else:
                visit_href = reverse(
                    url,
                    kwargs=dict(
                        appointment=str(visit.appointment.id),
                        subject_identifier=self.registered_subject.subject_identifier,
                    ),
                )

        template_name = "edc_data_manager/columns/query_text.html"
        context = dict(
            form_and_numbers=self.form_and_numbers_to_string(),
            query_priority=self.query_priority,
            query_priority_display=self.get_query_priority_display(),
            query_text=self.query_text,
            questions=self.data_dictionaries.all().order_by("model", "number"),
            report_datetime=self.report_datetime,
            requisition_panel=self.requisition_panel,
            resolved_datetime=self.resolved_datetime,
            site_resolved_datetime=self.site_resolved_datetime,
            site_response_text=self.site_response_text,
            site_response_status=self.get_site_response_status_display(),
            status=self.status,
            dm_user=self.dm_user,
            title=self.title,
            visit_schedule=self.visit_schedule,
            visit_href=visit_href,
        )
        return render_to_string(template_name, context=context)

    @property
    def model_names(self):
        model_names = list(set([dd.model for dd in self.data_dictionaries.all()]))
        return "|".join(model_names)

    class Meta(BaseUuidModel.Meta):
        verbose_name = "Data Query"
        verbose_name_plural = "Data Queries"
        unique_together = ("registered_subject", "rule_reference", "visit_schedule")
        indexes = (
            models.Index(
                fields=[
                    "subject_identifier",
                    "action_identifier",
                    "title",
                    "rule_reference",
                    "registered_subject",
                    "visit_schedule",
                ]
            ),
        )
