# -*- coding: utf-8 -*-
import datetime
import logging

from federation.fetchers import retrieve_remote_profile
from peewee import DateTimeField, CharField, IntegerField, BooleanField, TextField, DoesNotExist
from playhouse.fields import ManyToManyField

from social_relay import database
from social_relay.utils.text import safe_text


class StatisticBase(database.Model):
    """Base model for statistics."""
    created_at = DateTimeField(default=datetime.datetime.now)


class ReceiveStatistic(StatisticBase):
    """Received objects statistics."""
    sender_host = CharField()


class WorkerReceiveStatistic(StatisticBase):
    """Received processing statistics."""
    protocol = CharField()
    # How many entities contained
    entities = IntegerField(default=0)
    # How many remotes sent to
    sent_amount = IntegerField(default=0)
    # How many remote sends succeeded
    sent_success = IntegerField(default=0)


class Node(database.Model):
    """Stores Nodes, ie pods, servers, etc."""
    host = CharField(unique=True)
    created_at = DateTimeField(default=datetime.datetime.now)
    last_success = DateTimeField(null=True)
    total_delivered = IntegerField(default=0)
    failure_count = IntegerField(default=0)
    https = BooleanField(default=False)


# TODO remove in next release after migration drops it
class Post(database.Model):
    """Stores list of domains where posts have been delivered to."""
    nodes = ManyToManyField(Node, related_name="posts")
    guid = CharField()
    protocol = CharField()
    created_at = DateTimeField(default=datetime.datetime.now)

    class Meta:
        indexes = (
            (("guid", "protocol"), True),
        )


# TODO remove in next release after migration drops it
NodePost = Post.nodes.get_through_model()


class Profile(database.Model):
    """Store profile public keys."""
    identifier = CharField(unique=True)
    public_key = TextField()
    created_at = DateTimeField(default=datetime.datetime.now)

    @staticmethod
    def get_public_key(identifier):
        profile = Profile.get_profile(identifier)
        if profile:
            return profile.public_key

    @staticmethod
    def get_profile(identifier):
        """Get or create remote profile.

        Fetch it from federation layer if necessary or if the public key is empty for some reason.
        """
        try:
            sender_profile = Profile.get(Profile.identifier == identifier)
            if not sender_profile.public_key:
                raise DoesNotExist
        except DoesNotExist:
            remote_profile = retrieve_remote_profile(identifier)
            if not remote_profile:
                logging.warning("Remote profile %s not found locally or remotely.", identifier)
                return
            sender_profile = Profile.from_remote_profile(remote_profile)
        return sender_profile

    @staticmethod
    def from_remote_profile(remote_profile):
        """Create a Profile from a remote Profile entity."""
        public_key = safe_text(remote_profile.public_key)
        profile, created = Profile.get_or_create(
            identifier=safe_text(remote_profile.handle),
            defaults={
                "public_key": public_key,
            },
        )
        if not created and profile.public_key != public_key:
            profile.public_key = public_key
            profile.save()
        return profile


TABLES = (
    ReceiveStatistic, WorkerReceiveStatistic, Node, Post, NodePost, Profile,
)


def create_all_tables(db):
    """Create all tables to the designated db."""
    db.database.create_tables(TABLES)


def drop_all_tables(db):
    """Drop all tables from the designated db."""
    db.database.drop_tables(TABLES)