from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import func, text
from datetime import datetime
from re import compile
import uuid

from app import db
from Models import Base, LastReadMessageModel
from .AnalyticsModel import todays_analytics
from Rest.UserRegex import get_username_regex
import Models.UserModel as UM
import Models.ReactionModel as RM

re_username_finder = compile('^' + get_username_regex() + '( |\. |$|,|:)')
re_username = compile('^' + get_username_regex())


class PostModel(Base):
    __tablename__ = "post"

    type = db.Column(db.String, nullable=False)

    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': "post",
    }

    id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    channel_id = db.Column(db.ForeignKey('channel.id'), nullable=False)
    channel = db.relationship(
        "ChannelModel",
        back_populates="posts",
        foreign_keys=channel_id
    )
    author_id = db.Column(db.ForeignKey('user.id'), nullable=False)
    author = db.relationship(
        "UserModel"
    )
    anonymous = db.Column(db.Boolean, nullable=False, default=False)
    date_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
    _content = db.Column(db.String, nullable=False)
    linked_user = db.Column(db.ARRAY(UUID(as_uuid=True)), nullable=True)
    image_id = db.Column(db.ForeignKey('image.id'), nullable=True, default=None)
    image = db.relationship(
        "ImageModel"
    )

    def __init__(self, content="", **kwargs):
        self.date_created = datetime.utcnow()
        super().__init__(_content=content, **kwargs)
        todays_analytics().post_count += 1
        self.content = content

    def get_reactions(self, user=None):
        reactions = []
        r = db.session \
            .query(RM.ReactionModel.emoji, func.count(RM.ReactionModel.emoji).label("count")) \
            .filter(RM.ReactionModel.post == self) \
            .group_by(RM.ReactionModel.emoji) \
            .all()
        for (emoji, count) in r:
            if user and RM.ReactionModel.query.filter(RM.ReactionModel.user == user,
                                                      RM.ReactionModel.emoji == emoji,
                                                      RM.ReactionModel.post == self).first():
                reactions.append({
                    'emoji': emoji,
                    'count': count,
                    'self': True})
            else:
                reactions.append({
                    'emoji': emoji,
                    'count': count,
                    'self': False})
        return reactions

    def get_is_blocked(self, user):
        block = []
        if not self.anonymous and self.author in user.blocked_user:
            block.append("user")
        if self in user.blocked_posts:
            block.append("post")
        return block

    @property
    def content(self):
        return self._inject_linked_users_usernames(self._content)

    @content.setter
    def content(self, text):
        text = self._set_linked_user(text)
        self._content = text
        db.session.commit()

    def _set_linked_user(self, text):
        linked_user = []
        parts = text.split('@')
        new_text = parts[0]
        for substring in parts[1:]:
            if bool(re_username_finder.match(substring)):
                end = re_username.match(substring).end(0)
                user = UM.UserModel.query.filter(UM.UserModel.username == substring[0:end]).first()
                if user:
                    linked_user.append(user.id)
                    new_text += '@' + substring[end:]
                else:
                    linked_user.append(None)
                    new_text += '@' + substring
            else:
                linked_user.append(None)
                new_text += '@' + substring
        self.linked_user = linked_user
        return new_text

    def _inject_linked_users_usernames(self, text):
        parts = text.split('@')
        new_text = parts[0]
        for i in range(len(parts) - 1):
            new_text += '@'
            if self.linked_user and len(self.linked_user) > i and self.linked_user[i]:
                user = UM.UserModel.query.get(self.linked_user[i])
                if user:
                    new_text += user.username
                else:
                    self._remove_link_to_deleted_user(self.linked_user[i])
                    return self._inject_linked_users_usernames(text)
            new_text += parts[i + 1]
        return new_text

    def _remove_link_to_deleted_user(self, user_id):
        parts = self._content.split('@')
        index = self.linked_user.index(user_id)
        linked_user = self.linked_user.copy()
        linked_user[index] = None
        self.linked_user = linked_user
        parts[index + 1] = "deleted_user" + parts[index + 1]
        self._content = '@'.join(parts)
        db.session.commit()

    def delete(self):
        posts_before = {}
        last_messages = LastReadMessageModel.query.filter(LastReadMessageModel.post == self).all()
        for last_m in last_messages:
            if last_m.channel_id not in posts_before:
                posts_before[last_m.channel_id] = self.channel.post_before(self)
            last_m.post = posts_before[last_m.channel_id]

        db.engine.execute(text(f"DELETE FROM reaction WHERE post_id = '{self.id}';"))
        db.session.delete(self)
        db.session.commit()
