# Standard library imports
from __future__ import annotations
from dataclasses import dataclass
import datetime as dt
from enum import auto, Enum
import functools
import threading
from typing import NamedTuple

# Third party imports

# PyQt imports
from PyQt6.QtCore import (
    pyqtSignal,
    QObject,
)
from PyQt6.QtWidgets import (
    QProxyStyle,
    QStyle,
    QStyleOption,
)

# App imports


# Define singleton first as it's needed below
def singleton(cls):
    """
    Make a class a Singleton class (see
    https://realpython.com/primer-on-python-decorators/#creating-singletons)

    Added locking.
    """

    lock = threading.Lock()

    @functools.wraps(cls)
    def wrapper_singleton(*args, **kwargs):
        if wrapper_singleton.instance is None:
            with lock:
                if wrapper_singleton.instance is None:  # Check still None
                    wrapper_singleton.instance = cls(*args, **kwargs)
        return wrapper_singleton.instance

    wrapper_singleton.instance = None
    return wrapper_singleton


class ApplicationError(Exception):
    """
    Custom exception
    """

    pass


class AudioMetadata(NamedTuple):
    start_gap: int = 0
    silence_at: int = 0
    fade_at: int = 0


class Col(Enum):
    START_GAP = 0
    TITLE = auto()
    ARTIST = auto()
    INTRO = auto()
    DURATION = auto()
    START_TIME = auto()
    END_TIME = auto()
    LAST_PLAYED = auto()
    BITRATE = auto()
    NOTE = auto()


class FileErrors(NamedTuple):
    path: str
    error: str


@dataclass
class Filter:
    version: int = 1
    path_type: str = "contains"
    path: str = ""
    last_played_number: int = 0
    last_played_comparator: str = "before"
    last_played_unit: str = "years"
    duration_type: str = "longer than"
    duration_number: int = 0
    duration_unit: str = "minutes"


class PlaylistStyle(QProxyStyle):
    def drawPrimitive(self, element, option, painter, widget=None):
        """
        Draw a line across the entire row rather than just the column
        we're hovering over.
        """
        if (
            element == QStyle.PrimitiveElement.PE_IndicatorItemViewItemDrop
            and not option.rect.isNull()
        ):
            option_new = QStyleOption(option)
            option_new.rect.setLeft(0)
            if widget:
                option_new.rect.setRight(widget.width())
            option = option_new
        super().drawPrimitive(element, option, painter, widget)


class QueryCol(Enum):
    TITLE = 0
    ARTIST = auto()
    DURATION = auto()
    LAST_PLAYED = auto()
    BITRATE = auto()


class Tags(NamedTuple):
    artist: str = ""
    title: str = ""
    bitrate: int = 0
    duration: int = 0


@dataclass
class PlaylistDTO:
    name: str
    playlist_id: int
    favourite: bool = False
    is_template: bool = False
    open: bool = False


@dataclass
class TrackDTO:
    track_id: int
    artist: str
    bitrate: int
    duration: int
    fade_at: int
    intro: int | None
    path: str
    silence_at: int
    start_gap: int
    title: str
    lastplayed: dt.datetime | None


@dataclass
class PlaylistRowDTO(TrackDTO):
    note: str
    played: bool
    playlist_id: int
    playlistrow_id: int
    row_number: int


class TrackInfo(NamedTuple):
    track_id: int
    row_number: int


# Classes for signals
@dataclass
class InsertTrack:
    playlist_id: int
    track_id: int | None
    note: str


@singleton
@dataclass
class MusicMusterSignals(QObject):
    """
    Class for all MusicMuster signals. See:
     - https://zetcode.com/gui/pyqt5/eventssignals/
     - https://stackoverflow.com/questions/62654525/emit-a-signal-from-another-class-to-main-class
    """

    begin_reset_model_signal = pyqtSignal(int)
    enable_escape_signal = pyqtSignal(bool)
    end_reset_model_signal = pyqtSignal(int)
    next_track_changed_signal = pyqtSignal()
    resize_rows_signal = pyqtSignal(int)
    search_songfacts_signal = pyqtSignal(str)
    search_wikipedia_signal = pyqtSignal(str)
    show_warning_signal = pyqtSignal(str, str)
    signal_add_track_to_header = pyqtSignal(int, int)
    signal_insert_track = pyqtSignal(InsertTrack)
    signal_playlist_selected_rows = pyqtSignal(int, list)
    signal_set_next_row = pyqtSignal(int)
    # signal_set_next_track takes a PlaylistRow as an argument. We can't
    # specify that here as it requires us to import PlaylistRow from
    # playlistrow.py, which itself imports MusicMusterSignals
    signal_set_next_track = pyqtSignal(object)
    span_cells_signal = pyqtSignal(int, int, int, int, int)
    status_message_signal = pyqtSignal(str, int)
    track_ended_signal = pyqtSignal()

    def __post_init__(self):
        super().__init__()
