import logging
import os
from typing import List, Optional

from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QLabel, QPushButton,
    QListWidget, QListWidgetItem, QMenu, QInputDialog, QMessageBox,
    QFrame,
)
from PyQt6.QtCore import Qt, pyqtSignal, QUrl
from PyQt6.QtGui import QColor
from PyQt6.QtGui import QDragEnterEvent, QDragMoveEvent, QDragLeaveEvent, QDropEvent

logger = logging.getLogger(__name__)

SIDEBAR_MIN_WIDTH = 48
SIDEBAR_MAX_WIDTH = 240
SIDEBAR_DEFAULT_WIDTH = 200


class PlaylistDropListWidget(QListWidget):
    """QListWidget that accepts external track drops onto playlist items."""
    tracks_dropped_on_playlist = pyqtSignal(str, list)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self._hovered_item = None

    def mimeData(self, items):
        mime = super().mimeData(items)
        if items:
            playlist_id = items[0].data(Qt.ItemDataRole.UserRole)
            if playlist_id:
                mime.setData("application/x-neopod-playlist", playlist_id.encode())
        return mime

    def dragEnterEvent(self, event: QDragEnterEvent):
        if event.source() is not self and event.mimeData().hasUrls():
            event.acceptProposedAction()
        else:
            super().dragEnterEvent(event)

    def dragMoveEvent(self, event: QDragMoveEvent):
        if event.source() is not self and event.mimeData().hasUrls():
            pos = event.position().toPoint()
            item = self.itemAt(pos)
            if item != self._hovered_item:
                self._clear_hover()
                if item:
                    self._hovered_item = item
                    bg = self.palette().color(self.palette().ColorRole.Highlight)
                    c = bg.name()
                    item.setBackground(bg)
            event.acceptProposedAction()
        else:
            super().dragMoveEvent(event)

    def dragLeaveEvent(self, event: QDragLeaveEvent):
        self._clear_hover()
        super().dragLeaveEvent(event)

    def dropEvent(self, event: QDropEvent):
        self._clear_hover()
        if event.source() is not self and event.mimeData().hasUrls():
            pos = event.position().toPoint()
            item = self.itemAt(pos)
            if item is None:
                event.ignore()
                return
            playlist_id = item.data(Qt.ItemDataRole.UserRole)
            if not playlist_id:
                event.ignore()
                return
            paths = [url.toLocalFile() for url in event.mimeData().urls()]
            paths = [p for p in paths if os.path.exists(p)]
            if paths:
                self.tracks_dropped_on_playlist.emit(playlist_id, paths)
                event.acceptProposedAction()
            else:
                event.ignore()
        else:
            super().dropEvent(event)

    def _clear_hover(self):
        if self._hovered_item:
            self._hovered_item.setData(Qt.ItemDataRole.BackgroundRole, None)
            self._hovered_item = None


class NavDropListWidget(QListWidget):
    """QListWidget that accepts external track drops on the iPod nav item."""
    tracks_dropped_for_transfer = pyqtSignal(list)
    playlist_dropped_for_transfer = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self._hovered_item = None

    def _target_item_at(self, pos):
        item = self.itemAt(pos)
        if item and item.data(Qt.ItemDataRole.UserRole + 2) == "ipod":
            return item
        return None

    def _repolish(self):
        self.style().unpolish(self)
        self.style().polish(self)

    def dragEnterEvent(self, event: QDragEnterEvent):
        if event.mimeData().hasFormat("application/x-neopod-playlist"):
            self.setProperty("dndActive", True)
            self._repolish()
            pos = event.position().toPoint()
            if self._target_item_at(pos):
                event.acceptProposedAction()
                return
            # Let default handler accept so dragMoveEvent still fires
        if event.source() is not self and event.mimeData().hasUrls():
            pos = event.position().toPoint()
            if self._target_item_at(pos):
                event.acceptProposedAction()
                return
        super().dragEnterEvent(event)

    def dragMoveEvent(self, event: QDragMoveEvent):
        accept = False
        if event.source() is not self and event.mimeData().hasUrls():
            pos = event.position().toPoint()
            item = self._target_item_at(pos)
            if item != self._hovered_item:
                self._clear_hover()
                if item:
                    self._hovered_item = item
                    bg = self.palette().color(self.palette().ColorRole.Highlight)
                    item.setBackground(bg)
            if item:
                event.acceptProposedAction()
                accept = True
        if not accept and event.mimeData().hasFormat("application/x-neopod-playlist"):
            pos = event.position().toPoint()
            item = self._target_item_at(pos)
            if item != self._hovered_item:
                self._clear_hover()
                if item:
                    self._hovered_item = item
                    bg = self.palette().color(self.palette().ColorRole.Highlight)
                    item.setBackground(bg)
            if item:
                event.acceptProposedAction()
                accept = True
        if not accept:
            event.ignore()

    def dragLeaveEvent(self, event: QDragLeaveEvent):
        self._clear_hover()
        if self.property("dndActive"):
            self.setProperty("dndActive", False)
            self._repolish()
        super().dragLeaveEvent(event)

    def dropEvent(self, event: QDropEvent):
        self._clear_hover()
        if self.property("dndActive"):
            self.setProperty("dndActive", False)
            self._repolish()
        if event.source() is not self and event.mimeData().hasUrls():
            pos = event.position().toPoint()
            if self._target_item_at(pos):
                paths = [url.toLocalFile() for url in event.mimeData().urls()]
                paths = [p for p in paths if os.path.exists(p)]
                if paths:
                    self.tracks_dropped_for_transfer.emit(paths)
                    event.acceptProposedAction()
                    return
        if event.mimeData().hasFormat("application/x-neopod-playlist"):
            pos = event.position().toPoint()
            if self._target_item_at(pos):
                playlist_id = bytes(event.mimeData().data("application/x-neopod-playlist")).decode()
                if playlist_id:
                    self.playlist_dropped_for_transfer.emit(playlist_id)
                    event.acceptProposedAction()
                    return
        super().dropEvent(event)

    def _clear_hover(self):
        if self._hovered_item:
            self._hovered_item.setData(Qt.ItemDataRole.BackgroundRole, None)
            self._hovered_item = None


class SidebarWidget(QWidget):
    item_selected = pyqtSignal(str, str)
    playlist_selected = pyqtSignal(str)
    tracks_dropped_on_playlist = pyqtSignal(str, list)
    tracks_dropped_for_nav_transfer = pyqtSignal(list)
    playlist_dropped_for_nav_transfer = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setObjectName("sidebar")
        self.setMinimumWidth(SIDEBAR_MIN_WIDTH)
        self.setMaximumWidth(SIDEBAR_MAX_WIDTH)
        self.setFixedWidth(SIDEBAR_DEFAULT_WIDTH)
        self.setAutoFillBackground(True)

        p = self.palette()
        self._bg = p.color(p.ColorRole.Window)
        self._text = p.color(p.ColorRole.WindowText)
        self._dim_text = p.color(p.ColorRole.PlaceholderText)
        if not self._dim_text.isValid():
            self._dim_text = self._text.darker(160)
        self._border = p.color(p.ColorRole.Midlight)
        if not self._border.isValid():
            self._border = self._bg.darker(120)

        hl = p.color(p.ColorRole.Highlight)
        hl_light = QColor(hl)
        hl_light.setAlpha(60)
        hl_text = p.color(p.ColorRole.HighlightedText)

        self._apply_palette_style(hl, hl_light, hl_text)
        self._build_ui()

    def _apply_palette_style(self, hl: QColor, hl_light: QColor, hl_text: QColor):
        bg_name = self._bg.name()
        border_name = self._border.name()
        dim_name = self._dim_text.name()
        text_name = self._text.name()
        hl_name = hl.name()
        hl_light_name = hl_light.name()
        hl_text_name = hl_text.name()

        self.setStyleSheet(f"""
            QWidget#sidebar {{
                background: {bg_name};
                border-right: 1px solid {border_name};
            }}
            QListWidget {{
                background: transparent;
                border: none;
                outline: none;
                font-size: 13px;
                color: {text_name};
            }}
            QListWidget::item {{
                padding: 4px 12px;
                border-radius: 4px;
            }}
            QListWidget::item:selected {{
                background: {hl_name};
                color: {hl_text_name};
            }}
            QListWidget::item:hover:!selected {{
                background: {hl_light_name};
            }}
            #navList[dndActive="true"]::item:hover:!selected {{
                background: transparent;
            }}
        """)

    def _build_ui(self):
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 8, 0, 8)
        layout.setSpacing(0)

        dim_color = self._dim_text.name()
        section_style = f"""
            color: {dim_color};
            font-size: 11px;
            font-weight: bold;
            letter-spacing: 1px;
            padding: 8px 12px 4px 12px;
            background: transparent;
        """

        header = QLabel("LIBRARY")
        header.setStyleSheet(section_style)
        layout.addWidget(header)

        self._library_list = QListWidget()
        items = [
            ("\U0001F4BF  Music", "tab", "library"),
            ("\u23F1  Recently Added", "filter", "recent"),
            ("\U0001F3A4  Artists", "filter", "artists"),
            ("\U0001F4BF  Albums", "filter", "albums"),
            ("\U0001F3B5  Songs", "filter", "songs"),
        ]
        for label, kind, value in items:
            item = QListWidgetItem(label)
            item.setData(Qt.ItemDataRole.UserRole + 1, kind)
            item.setData(Qt.ItemDataRole.UserRole + 2, value)
            self._library_list.addItem(item)
        self._library_list.itemClicked.connect(self._on_library_item_clicked)
        self._library_list.setCurrentRow(0)
        layout.addWidget(self._library_list)

        line = QFrame()
        line.setFrameShape(QFrame.Shape.HLine)
        line.setStyleSheet(f"color: {self._border.name()}; margin: 4px 12px;")
        line.setFixedHeight(1)
        layout.addWidget(line)

        self._playlists_header = QLabel("PLAYLISTS")
        self._playlists_header.setStyleSheet(section_style)
        layout.addWidget(self._playlists_header)

        self._playlist_list = PlaylistDropListWidget()
        self._playlist_list.setDragDropMode(QListWidget.DragDropMode.InternalMove)
        self._playlist_list.setDefaultDropAction(Qt.DropAction.MoveAction)
        self._playlist_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
        self._playlist_list.customContextMenuRequested.connect(
            self._on_playlist_context_menu
        )
        self._playlist_list.itemClicked.connect(self._on_playlist_clicked)
        self._playlist_list.model().rowsMoved.connect(self._on_playlists_reordered)
        self._playlist_list.tracks_dropped_on_playlist.connect(self._on_tracks_dropped_on_playlist)
        layout.addWidget(self._playlist_list, stretch=1)

        btn_style = f"""
            QPushButton {{
                text-align: left;
                padding: 6px 12px;
                border: none;
                color: {dim_color};
                font-size: 12px;
            }}
            QPushButton:hover {{
                color: {self._text.name()};
                background: {self._border.name()};
            }}
        """
        self._new_playlist_btn = QPushButton("  \u2795  New Playlist")
        self._new_playlist_btn.setFlat(True)
        self._new_playlist_btn.setCursor(Qt.CursorShape.PointingHandCursor)
        self._new_playlist_btn.setStyleSheet(btn_style)
        self._new_playlist_btn.clicked.connect(self._on_new_playlist)
        layout.addWidget(self._new_playlist_btn)

        line2 = QFrame()
        line2.setFrameShape(QFrame.Shape.HLine)
        line2.setStyleSheet(f"color: {self._border.name()}; margin: 4px 12px;")
        line2.setFixedHeight(1)
        layout.addWidget(line2)

        self._nav_list = NavDropListWidget()
        self._nav_list.setObjectName("navList")
        items = [
            ("🎸  iPod", "ipod"),
            ("⚙️  Settings", "settings"),
        ]
        for label, tab_id in items:
            item = QListWidgetItem(label)
            item.setData(Qt.ItemDataRole.UserRole + 1, "tab")
            item.setData(Qt.ItemDataRole.UserRole + 2, tab_id)
            self._nav_list.addItem(item)
        self._nav_list.itemClicked.connect(self._on_nav_item_clicked)
        self._nav_list.tracks_dropped_for_transfer.connect(self._on_tracks_dropped_for_nav_transfer)
        self._nav_list.playlist_dropped_for_transfer.connect(self._on_playlist_dropped_for_transfer)
        layout.addWidget(self._nav_list)

        self._playlist_items: dict = {}

    def _on_library_item_clicked(self, item: QListWidgetItem):
        kind = item.data(Qt.ItemDataRole.UserRole + 1)
        value = item.data(Qt.ItemDataRole.UserRole + 2)
        self._library_list.setCurrentItem(item)
        self._playlist_list.clearSelection()
        self._nav_list.clearSelection()
        self.item_selected.emit(kind, value)

    def _on_playlist_clicked(self, item: QListWidgetItem):
        playlist_id = item.data(Qt.ItemDataRole.UserRole)
        self._library_list.clearSelection()
        self._playlist_list.setCurrentItem(item)
        self._nav_list.clearSelection()
        self.playlist_selected.emit(playlist_id)

    def _on_playlist_context_menu(self, pos):
        item = self._playlist_list.itemAt(pos)
        if item is None:
            return
        menu = QMenu(self)
        rename_action = menu.addAction("Rename")
        delete_action = menu.addAction("Delete")

        action = menu.exec(self._playlist_list.viewport().mapToGlobal(pos))
        playlist_id = item.data(Qt.ItemDataRole.UserRole)
        if action == rename_action:
            self._rename_playlist(item, playlist_id)
        elif action == delete_action:
            self._delete_playlist(item, playlist_id)

    def _rename_playlist(self, item: QListWidgetItem, playlist_id: str):
        current = item.text()
        name, ok = QInputDialog.getText(
            self, "Rename Playlist", "New name:", text=current
        )
        if ok and name.strip():
            item.setText(name.strip())
            self.item_selected.emit("playlist_rename", playlist_id + "|" + name.strip())

    def _delete_playlist(self, item: QListWidgetItem, playlist_id: str):
        reply = QMessageBox.question(
            self, "Delete Playlist",
            f'Delete playlist "{item.text()}"?\nThis cannot be undone.',
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
        )
        if reply == QMessageBox.StandardButton.Yes:
            row = self._playlist_list.row(item)
            self._playlist_list.takeItem(row)
            self.item_selected.emit("playlist_delete", playlist_id)

    def _on_new_playlist(self):
        name, ok = QInputDialog.getText(self, "New Playlist", "Playlist name:")
        if ok and name.strip():
            self.item_selected.emit("playlist_create", name.strip())

    def add_playlist_item(self, playlist_id: str, name: str):
        item = QListWidgetItem(f"\U0001F4CB  {name}")
        item.setData(Qt.ItemDataRole.UserRole, playlist_id)
        self._playlist_list.addItem(item)
        self._playlist_items[playlist_id] = item

    def remove_playlist_item(self, playlist_id: str):
        item = self._playlist_items.pop(playlist_id, None)
        if item:
            row = self._playlist_list.row(item)
            self._playlist_list.takeItem(row)

    def rename_playlist_item(self, playlist_id: str, new_name: str):
        item = self._playlist_items.get(playlist_id)
        if item:
            item.setText(f"\U0001F4CB  {new_name}")

    def clear_playlist_items(self):
        self._playlist_list.clear()
        self._playlist_items.clear()

    def get_playlist_order_ids(self) -> List[str]:
        ids = []
        for i in range(self._playlist_list.count()):
            item = self._playlist_list.item(i)
            ids.append(item.data(Qt.ItemDataRole.UserRole))
        return ids

    def _on_tracks_dropped_on_playlist(self, playlist_id: str, paths: list):
        self.tracks_dropped_on_playlist.emit(playlist_id, paths)

    def _on_tracks_dropped_for_nav_transfer(self, paths: list):
        self.tracks_dropped_for_nav_transfer.emit(paths)

    def _on_playlist_dropped_for_transfer(self, playlist_id: str):
        self.playlist_dropped_for_nav_transfer.emit(playlist_id)

    def _on_playlists_reordered(self):
        order = self.get_playlist_order_ids()
        self.item_selected.emit("playlist_reorder", "|".join(order))

    def _on_nav_item_clicked(self, item: QListWidgetItem):
        kind = item.data(Qt.ItemDataRole.UserRole + 1)
        value = item.data(Qt.ItemDataRole.UserRole + 2)
        self._library_list.clearSelection()
        self._playlist_list.clearSelection()
        self._nav_list.setCurrentItem(item)
        self.item_selected.emit(kind, value)

    def select_library_music(self):
        if self._library_list.count() > 0:
            self._library_list.setCurrentRow(0)
            self._on_library_item_clicked(self._library_list.item(0))

    def select_playlist(self, playlist_id: str):
        for i in range(self._playlist_list.count()):
            item = self._playlist_list.item(i)
            if item.data(Qt.ItemDataRole.UserRole) == playlist_id:
                self._playlist_list.setCurrentItem(item)
                self._on_playlist_clicked(item)
                return

    def select_playlist_silent(self, playlist_id: str):
        for i in range(self._playlist_list.count()):
            item = self._playlist_list.item(i)
            if item.data(Qt.ItemDataRole.UserRole) == playlist_id:
                self._playlist_list.setCurrentItem(item)
                self._library_list.clearSelection()
                self._nav_list.clearSelection()
                return

    def select_by_view_mode(self, mode: str):
        mapping = {"songs": 4, "albums": 3, "artists": 2, "recent": 1}
        row = mapping.get(mode, 0)
        if row < self._library_list.count():
            self._library_list.setCurrentRow(row)
        self._playlist_list.clearSelection()
        self._nav_list.clearSelection()

    def width(self) -> int:
        return self.geometry().width()
