import logging

from model_utils.models import TimeStampedModel

from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import JSONField
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from lms.core.models.mixins import ActiveModelMixin
from lms.courses.models import Course

log = logging.getLogger(__name__)
User = get_user_model()


class ActionEvent(TimeStampedModel):
    class Status(models.TextChoices):
        PENDING = 'pending', _("Ожидает")
        PROCESSING = 'processing', _("В обработке")
        COMPLETED = 'completed', _("Завершено")
        CANCELLED = 'cancelled', _("Отменено")
        ERROR = 'error', _("Ошибка")

    status = models.CharField(
        _("статус"),
        max_length=20,
        choices=Status.choices,
        default=Status.PENDING
    )

    description = models.TextField(_("описание"))
    message = models.TextField(_("результат действия"), blank=True)

    class EventType(models.TextChoices):
        ADD_ACHIEVEMENT = 'add_achievement', _("Выдача ачивки")

    event_type = models.CharField(
        _("тип действия"),
        max_length=50,
        choices=EventType.choices,
        default=EventType.ADD_ACHIEVEMENT
    )

    completion_date = models.DateTimeField(
        verbose_name=_("дата завершения действия"),
        null=True,
        blank=True,
        db_index=True,
    )

    class Meta:
        verbose_name = _("событие")
        verbose_name_plural = _("события")

    def process(self):
        raise NotImplementedError()

    def save(self, *args, **kwargs):
        if self.status == self.Status.COMPLETED and not self.completion_date:
            self.completion_date = timezone.now()
        super().save(*args, **kwargs)


class AddAchievementEvent(ActionEvent):
    achievement_id = models.IntegerField(_("ID ачивки"))
    level = models.IntegerField(_("уровень ачивки"), null=True, blank=True)

    user = models.ForeignKey(
        User,
        verbose_name=_("пользователь"),
        related_name="add_achievement_events",
        on_delete=models.PROTECT,
    )

    course = models.ForeignKey(
        Course,
        verbose_name=_("курс"),
        related_name="add_achievement_events",
        on_delete=models.PROTECT,
    )

    class Meta:
        verbose_name = _("выдача ачивки")
        verbose_name_plural = _("выдача ачивок")

    def process(self):
        from lms.actions.services import add_achievement
        add_achievement(self)

    def save(self, *args, **kwargs):
        self.event_type = self.EventType.ADD_ACHIEVEMENT
        super().save(*args, **kwargs)


class CourseTrigger(TimeStampedModel, ActiveModelMixin):
    class ActionType(models.TextChoices):
        ADD_ACHIEVEMENT = 'add_achievement', _("Выдать ачивку")

    class TriggerType(models.TextChoices):
        COURSE_PASSED = 'course_passed', _("Курс пройден")

    name = models.CharField(_("название"), max_length=255)
    description = models.TextField(_("описание"), blank=True)

    course = models.ForeignKey(
        Course,
        verbose_name=_("курс"),
        related_name='triggers',
        on_delete=models.PROTECT
    )

    action_type = models.CharField(
        _("тип действия"),
        max_length=50,
        choices=ActionType.choices,
    )

    trigger_type = models.CharField(
        _("тип триггера"),
        max_length=50,
        choices=TriggerType.choices,
    )

    parameters = JSONField(_("параметры"), default=dict)

    class Meta:
        verbose_name = _("триггер курса")
        verbose_name_plural = _("триггеры курсов")

    def __init__(self, *args, **kwargs):
        from .services import create_add_achivement_event

        def add_achievement_validator(instance):
            achievement_id = instance.parameters.get('achievement_id')
            if not achievement_id:
                raise ValidationError(
                    _(f'в параметрах триггера с типом действия "{instance.action_type}" не указан achievement_id'),
                    code='invalid',
                )

            try:
                int(achievement_id)
            except ValueError:
                raise ValidationError(
                    _(f'неверный формат параметра "achievement_id" ({achievement_id}): ожидается int'),
                    code='invalid',
                )

        super().__init__(*args, **kwargs)

        self.action_handler_map = {
            self.ActionType.ADD_ACHIEVEMENT: create_add_achivement_event,
        }

        self.action_validator_map = {
            self.ActionType.ADD_ACHIEVEMENT: add_achievement_validator,
        }

    def start(self, *args, **kwargs):
        action = self.action_handler_map.get(self.action_type)
        if not action:
            log.error(_(f"Не задана функция для действия триггера {self.action_type}"))
            return
        action(course_trigger=self, *args, **kwargs)

    def clean(self):
        super().clean()
        self.action_validator_map[self.action_type](self)

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)
