from sqlalchemy import func

from core import db
from core.models import MusicFolder, Track, Tag, TrackTag, SessionTrack, Session, SessionFolder


class Repository:
    MODEL_CLS = None

    def __init__(self, session=None):
        self.session = session if session else db.Session()

    def query(self):
        return self.session.query(self.MODEL_CLS)

    def commit(self):
        self.session.commit()

    def rollback(self):
        self.session.rollback()

    def get_by_id(self, id_):
        return self.query().filter(self.MODEL_CLS.id == id_).first()

    def get_all(self):
        return self.query().all()

    def get_by(self, field, val):
        return self.query().filter(field == val).all()

    def exists(self, field, val):
        return self.query().filter(field == val).first() is not None

    def create(self, model, commit=False):
        self.session.add(model)
        if commit:
            self.session.commit()

    def delete(self, model, commit=False):
        self.session.delete(model)
        if commit:
            self.commit()

    # def __del__(self):
    #     self.session.remove()


class MusicFolderRepository(Repository):
    MODEL_CLS = MusicFolder

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


class TagRepository(Repository):
    MODEL_CLS = Tag

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def get_by_track_id(self, track_id):
        return self.session.query(Tag)\
            .join(TrackTag, Tag.id == TrackTag.tag_id)\
            .filter(TrackTag.track_id == track_id).\
            all()


class TrackTagRepository(Repository):
    MODEL_CLS = TrackTag


class TrackRepository(Repository):
    MODEL_CLS = Track

    def get_all(self):
        return self.query().order_by(Track.artist).order_by(Track.album).order_by(Track.track_num).all()

    def get_by_hash(self, hash_):
        return self.query().filter(Track.hash == hash_).first()

    def get_by_tag_id(self, tag_id):
        return self.session.query(Track)\
            .join(TrackTag, Track.id == TrackTag.track_id)\
            .filter(TrackTag.tag_id == tag_id).\
            all()

    def get_by_tag_ids(self, tag_ids):
        return self.session.query(Track)\
            .join(TrackTag, Track.id == TrackTag.track_id)\
            .filter(TrackTag.tag_id.in_(tag_ids)).\
            all()

    def get_artists(self):
        return self.session.query(Track).group_by(Track.artist).all()

    def get_albums_by_artist(self, artist):
        return self.session.query(Track).filter(Track.artist == artist).group_by(Track.album).all()

    def get_by_artist_and_album(self, artist, album):
        return self.session.query(Track)\
            .filter(Track.artist == artist)\
            .filter(Track.album == album)\
            .group_by(Track.album)\
            .all()

    def add_to_session(self, track_id, session_id):
        track_session = SessionTrack()
        track_session.track_id = track_id
        track_session.session_id = session_id
        row = self.session.query(func.max(SessionTrack.order)).filter(SessionTrack.session_id == session_id)
        order = (row.scalar() or 0) + 1
        track_session.order = order
        self.create(track_session)
        self.commit()

    def get_by_session_id(self, session_id):
        return self.session.query(Track) \
            .join(SessionTrack, Track.id == SessionTrack.track_id) \
            .filter(SessionTrack.session_id == session_id)\
            .order_by(SessionTrack.order)\
            .all()


class SessionTrackRepository(Repository):
    MODEL_CLS = SessionTrack

    def get_by_session_id(self, session_id):
        return self.session.query(SessionTrack)\
            .filter(SessionTrack.session_id == session_id)\
            .order_by(SessionTrack.order)\
            .all()

    def set_session_track_order(self, session_track, new_order, commit=False):
        if new_order == session_track.order:
            return

        session_tracks = self.session.query(SessionTrack)\
            .filter(SessionTrack.session_id == session_track.session_id)\
            .order_by(SessionTrack.order)\
            .all()

        max_order = max(st.order for st in session_tracks)

        new_order = max([new_order, 1])
        new_order = min([new_order, max_order])

        for other in session_tracks:
            if new_order > session_track.order and new_order >= other.order > session_track.order:
                other.order -= 1
            elif new_order < session_track.order and new_order <= other.order < session_track.order:
                other.order += 1

        if new_order < 1:
            new_order = 1
        elif new_order > max_order:
            new_order = max_order

        session_track.order = new_order

        if commit:
            self.commit()


class SessionFolderRepository(Repository):
    MODEL_CLS = SessionFolder

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


class SessionRepository(Repository):
    MODEL_CLS = Session

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def get_all(self):
        return self.query().order_by(Session.date.desc()).order_by(Session.created_on.desc()).all()

    def get_by_folder_id(self, folder_id):
        return self.get_by('folder_id', folder_id)

    def get_by_track_id(self, track_id):
        return self.session.query(Session)\
            .join(SessionTrack, Track.id == SessionTrack.track_id)\
            .filter(SessionTrack.track_id == track_id).\
            all()
