import uuid
import django.contrib.postgres.fields as postgres
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.core.serializers.json import DjangoJSONEncoder
from django.shortcuts import reverse
from common.models import Entity, Mark, Review, Tag, MarkStatusEnum
from common.utils import ChoicesDictGenerator, GenerateDateUUIDMediaFilePath
from django.utils import timezone
from django.conf import settings
from django.db.models import Q
import re


MovieMarkStatusTranslation = {
    MarkStatusEnum.DO.value: _("在看"),
    MarkStatusEnum.WISH.value: _("想看"),
    MarkStatusEnum.COLLECT.value: _("看过")
}


def movie_cover_path(instance, filename):
    return GenerateDateUUIDMediaFilePath(instance, filename, settings.MOVIE_MEDIA_PATH_ROOT)


class MovieGenreEnum(models.TextChoices):
    DRAMA = 'Drama', _('剧情')
    KIDS = 'Kids', _('儿童')
    COMEDY = 'Comedy', _('喜剧')
    BIOGRAPHY = 'Biography', _('传记')
    ACTION = 'Action', _('动作')
    HISTORY = 'History', _('历史')
    ROMANCE = 'Romance', _('爱情')
    WAR = 'War', _('战争')
    SCI_FI = 'Sci-Fi', _('科幻')
    CRIME = 'Crime', _('犯罪')
    ANIMATION = 'Animation', _('动画')
    WESTERN = 'Western', _('西部')
    MYSTERY = 'Mystery', _('悬疑')
    FANTASY = 'Fantasy', _('奇幻')
    THRILLER = 'Thriller', _('惊悚')
    ADVENTURE = 'Adventure', _('冒险')
    HORROR = 'Horror', _('恐怖')
    DISASTER = 'Disaster', _('灾难')
    DOCUMENTARY = 'Documentary', _('纪录片')
    MARTIAL_ARTS = 'Martial-Arts', _('武侠')
    SHORT = 'Short', _('短片')
    ANCIENT_COSTUM = 'Ancient-Costum', _('古装')
    EROTICA = 'Erotica', _('情色')
    SPORT = 'Sport', _('运动')
    GAY_LESBIAN = 'Gay/Lesbian', _('同性')
    OPERA = 'Opera', _('戏曲')
    MUSIC = 'Music', _('音乐')
    FILM_NOIR = 'Film-Noir', _('黑色电影')
    MUSICAL = 'Musical', _('歌舞')
    REALITY_TV = 'Reality-TV', _('真人秀')
    FAMILY = 'Family', _('家庭')
    TALK_SHOW = 'Talk-Show', _('脱口秀')
    NEWS = 'News', _('新闻')
    SOAP = 'Soap', _('肥皂剧')
    TV_MOVIE = 'TV Movie', _('电视电影')
    THEATRE = 'Theatre', _('舞台艺术')
    OTHER = 'Other', _('其他')


MovieGenreTranslator = ChoicesDictGenerator(MovieGenreEnum)


class Movie(Entity):
    '''
    Can either be movie or series.
    '''
    # widely recognized name, usually in Chinese
    title = models.CharField(_("title"), max_length=200)
    # original name, for books in foreign language
    orig_title = models.CharField(
        _("original title"), blank=True, default='', max_length=200)
    other_title = postgres.ArrayField(
        models.CharField(_("other title"), blank=True,
                         default='', max_length=300),
        null=True,
        blank=True,
        default=list,
    )
    imdb_code = models.CharField(
        blank=True, max_length=10, null=False, db_index=True, default='')
    director = postgres.ArrayField(
        models.CharField(_("director"), blank=True,
                         default='', max_length=100),
        null=True,
        blank=True,
        default=list,
    )
    playwright = postgres.ArrayField(
        models.CharField(_("playwright"), blank=True,
                         default='', max_length=100),
        null=True,
        blank=True,
        default=list,
    )
    actor = postgres.ArrayField(
        models.CharField(_("actor"), blank=True,
                         default='', max_length=100),
        null=True,
        blank=True,
        default=list,
    )
    genre = postgres.ArrayField(
        models.CharField(
            _("genre"),
            blank=True,
            default='',
            choices=MovieGenreEnum.choices,
            max_length=50
        ),
        null=True,
        blank=True,
        default=list,
    )
    showtime = postgres.ArrayField(
        # HStoreField stores showtime-region pair
        postgres.HStoreField(),
        null=True,
        blank=True,
        default=list,
    )
    site = models.URLField(_('site url'), blank=True, default='', max_length=200)
    
    # country or region
    area = postgres.ArrayField(
        models.CharField(
            _("country or region"),
            blank=True,
            default='',
            max_length=100,
        ),
        null=True,
        blank=True,
        default=list,
    )

    language = postgres.ArrayField(
        models.CharField(
            blank=True,
            default='',
            max_length=100,
        ),
        null=True,
        blank=True,
        default=list,
    )

    year = models.PositiveIntegerField(null=True, blank=True)
    duration = models.CharField(blank=True, default='', max_length=200)

    cover = models.ImageField(_("poster"), upload_to=movie_cover_path, default=settings.DEFAULT_MOVIE_IMAGE, blank=True)

    ############################################
    # exclusive fields to series
    ############################################
    season = models.PositiveSmallIntegerField(null=True, blank=True)
    # how many episodes in the season
    episodes = models.PositiveIntegerField(null=True, blank=True)
    # deprecated
    # tv_station = models.CharField(blank=True, default='', max_length=200)
    single_episode_length = models.CharField(blank=True, default='', max_length=100)

    ############################################
    # category identifier
    ############################################
    is_series = models.BooleanField(default=False)


    def __str__(self):
        if self.year:
            return self.title + f"({self.year})"  
        else:
            return self.title

    def get_json(self):
        r = {
            'other_title': self.other_title,
            'original_title': self.orig_title,
            'director': self.director,
            'playwright': self.playwright,
            'actor': self.actor,
            'release_year': self.year,
            'genre': self.genre,
            'language': self.language,
            'season': self.season,
            'duration': self.duration,
            'imdb_code': self.imdb_code,
        }
        r.update(super().get_json())
        return r

    def get_absolute_url(self):
        return reverse("movies:retrieve", args=[self.id])

    def get_tags_manager(self):
        return self.movie_tags

    def get_genre_display(self):
        translated_genre = []
        for g in self.genre:
            translated_genre.append(MovieGenreTranslator[g])
        return translated_genre

    def get_related_movies(self):
        imdb = 'no match' if self.imdb_code is None or self.imdb_code == '' else self.imdb_code
        qs = Q(imdb_code=imdb)
        if self.is_series:
            prefix = re.sub(r'\d+', '', re.sub(r'\s+第.+季', '', self.title))
            qs = qs | Q(title__startswith=prefix)
        qs = qs & ~Q(id=self.id)
        return Movie.objects.filter(qs).order_by('season')

    def get_identicals(self):
        qs = Q(orig_title=self.title)
        if self.imdb_code:
            qs = Q(imdb_code=self.imdb_code)
            # qs = qs & ~Q(id=self.id)
            return Movie.objects.filter(qs)
        else:
            return [self]  # Book.objects.filter(id=self.id)

    @property
    def verbose_category_name(self):
        if self.is_series:
            return _("剧集")
        else:
            return _("电影")

    @property
    def mark_class(self):
        return MovieMark

    @property
    def tag_class(self):
        return MovieTag


class MovieMark(Mark):
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_marks', null=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['owner', 'movie'], name='unique_movie_mark')
        ]

    @property
    def translated_status(self):
        return MovieMarkStatusTranslation[self.status]


class MovieReview(Review):
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_reviews', null=True)
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['owner', 'movie'], name='unique_movie_review')
        ]

    @property
    def url(self):
        return settings.APP_WEBSITE + reverse("movies:retrieve_review", args=[self.id])


class MovieTag(Tag):
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_tags', null=True)
    mark = models.ForeignKey(MovieMark, on_delete=models.CASCADE, related_name='moviemark_tags', null=True)
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['content', 'mark'], name="unique_moviemark_tag")
        ]

    @property
    def item(self):
        return self.movie