#
# Aura Engine API (https://gitlab.servus.at/aura/engine-api)
#
# Copyright (C) 2020 - The Aura Engine Team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import datetime

from sqlalchemy import create_engine, Column, DateTime, String, Integer, Boolean
from sqlalchemy.event import listen
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

db = SQLAlchemy()
ma = Marshmallow()


class PlayLog(db.Model):
    """
    Table holding play-log entries.
    """
    __tablename__ = 'playlog'

    # Primary Key
    track_start             = Column(DateTime, primary_key=True)

    # Columns
    track_artist            = Column(String(256))
    track_album             = Column(String(256))
    track_title             = Column(String(256))
    track_duration          = Column(String(256))
    track_type              = Column(Integer)
    timeslot_id             = Column(Integer)
    show_name               = Column(String(256))
    log_source              = Column(Integer)
    is_synced               = Column(Boolean)



    def __init__(self, data):
        """
        Initializes a trackservice entry
        """
        self.track_start     = data.track_start
        self.track_artist    = data.track_artist
        self.track_album     = data.track_album
        self.track_title     = data.track_title
        self.track_duration  = data.track_duration
        self.track_type      = data.track_type
        self.timeslot_id     = data.timeslot_id
        self.show_name       = data.show_name
        self.log_source      = data.log_source      # The play-out source this log is coming from (e.g. engine1, engine2)
        self.is_synced       = False                # Only relevant for main nodes, in a multi-node setup


    def save(self):
        db.session.add(self)
        db.session.commit()


    @staticmethod
    def select_current():
        """
        Selects the currently playing track.
        """
        db.session.commit()
        now = datetime.datetime.now()
        track = db.session.query(PlayLog).\
            filter(PlayLog.track_start <= str(now)).\
            order_by(PlayLog.track_start.desc()).first()
        return track


    @staticmethod
    def paginate(page, page_size, since_time=None):
        """
        Returns a list of entries for a given page and an start time (optional).
        """
        def q(page=0, page_size=None):
            query = db.session.query(PlayLog).order_by(PlayLog.track_start.desc())
            if isinstance(since_time, datetime.datetime):
                query = query.filter(PlayLog.track_start >= since_time)
            listen(query, 'before_compile', apply_limit(page, page_size), retval=True)
            return query

        def apply_limit(page, page_size):
            def wrapped(query):
                if page_size:
                    query = query.limit(page_size)
                    if page:
                        query = query.offset(page * page_size)
                return query
            return wrapped

        return q(page, page_size)


    @staticmethod
    def select_last_hours(n):
        """
        Selects the tracks playing in the past (`n`) hours.
        """
        db.session.commit()
        last_hours = datetime.datetime.today() - datetime.timedelta(hours=n)
        tracks = db.session.query(PlayLog).filter(PlayLog.track_start >= str(last_hours)).\
            order_by(PlayLog.track_start.desc()).all()
        return tracks


    @staticmethod
    def select_by_day(day):
        """
        Select the track-service items for a day.
        """
        db.session.commit()
        day_plus_one = day + datetime.timedelta(days=1)
        tracks = db.session.query(PlayLog).\
            filter(PlayLog.track_start >= str(day), PlayLog.track_start < str(day_plus_one)).\
            order_by(PlayLog.track_start.desc()).all()
        return tracks
    

    @staticmethod
    def select_by_range(from_day, to_day):
        """
        Selects the track-service items for a day range.
        """
        db.session.commit()
        tracks = db.session.query(PlayLog).filter(PlayLog.track_start >= str(from_day),\
            PlayLog.track_start < str(to_day)).order_by(PlayLog.track_start.desc()).all()
        return tracks


    def __str__(self):
        return "Track [track_start: %s, track_title: %s]" % (str(self.track_start), str(self.track_title))



class PlayLogSchema(ma.SQLAlchemyAutoSchema):
    """
    Schema for playlog entries.
    """
    class Meta:
        model = PlayLog
        sqla_session = db.session



class TrackSchema(ma.SQLAlchemySchema):
    """
    Schema for trackservice entries.
    """
    class Meta:
        model = PlayLog
        sqla_session = db.session
        fields = (
            "track_start",
            "track_artist",
            "track_album",
            "track_title"
            )



class ActivityLog(db.Model):
    """
    Table holding a log of play-out source active and sync states.
    Only used in "SYNC" deployment mode.
    """
    __tablename__ = 'activity_log'

    # Primary Key
    log_time             = Column(DateTime, primary_key=True)

    # Columns
    source_number        = Column(Integer)
    is_synced            = Column(Boolean)


    def __init__(self, source_number):
        """
        Initializes a activity entry
        """
        self.log_time = datetime.datetime.now()
        self.source_number = source_number
        self.is_synced = False


    def save(self):
        db.session.add(self)
        db.session.commit()



class HealthHistory(db.Model):
    """
    Table holding an history of health information for sources.
    """
    __tablename__ = 'health_history'

    # Primary Key
    log_time             = Column(DateTime, primary_key=True)

    # Columns
    source_number        = Column(Integer)
    is_healthy           = Column(Boolean)
    health_info          = Column(String(2048))


    def __init__(self, source_number):
        """
        Initializes an health entry.
        """
        self.log_time = datetime.datetime.now()
        self.source_number = source_number


    def save(self):
        db.session.add(self)
        db.session.commit()