#!/usr/bin/python3

from dataclasses import dataclass, field
from typing import Optional, Dict, Any, List, Union

from .types import ExternalUrls, Image, _str, _int
from .track import Track as TrackModel


@dataclass
class UserMini:
	id: Optional[str] = None
	type: str = "user"
	uri: Optional[str] = None
	display_name: Optional[str] = None
	external_urls: ExternalUrls = field(default_factory=ExternalUrls)

	@staticmethod
	def from_dict(obj: Any) -> "UserMini":
		if not isinstance(obj, dict):
			return UserMini()
		return UserMini(
			id=_str(obj.get("id")),
			type=_str(obj.get("type")) or "user",
			uri=_str(obj.get("uri")),
			display_name=_str(obj.get("display_name")),
			external_urls=ExternalUrls.from_dict(obj.get("external_urls", {})),
		)

	def to_dict(self) -> Dict[str, Any]:
		out = {
			"id": self.id,
			"type": self.type,
			"uri": self.uri,
			"display_name": self.display_name,
			"external_urls": self.external_urls.to_dict(),
		}
		return {k: v for k, v in out.items() if v not in (None, {}, [], "")}


@dataclass
class TrackStub:
	id: Optional[str] = None
	type: str = "track"
	uri: Optional[str] = None
	external_urls: ExternalUrls = field(default_factory=ExternalUrls)

	@staticmethod
	def from_dict(obj: Any) -> "TrackStub":
		if not isinstance(obj, dict):
			return TrackStub()
		return TrackStub(
			id=_str(obj.get("id")),
			type=_str(obj.get("type")) or "track",
			uri=_str(obj.get("uri")),
			external_urls=ExternalUrls.from_dict(obj.get("external_urls", {})),
		)

	def to_dict(self) -> Dict[str, Any]:
		out = {
			"id": self.id,
			"type": self.type,
			"uri": self.uri,
			"external_urls": self.external_urls.to_dict(),
		}
		return {k: v for k, v in out.items() if v not in (None, {}, [], "")}


@dataclass
class PlaylistItem:
	added_at: Optional[str] = None
	added_by: UserMini = field(default_factory=UserMini)
	is_local: bool = False
	track: Optional[Union[TrackModel, TrackStub]] = None
	item_id: Optional[str] = None

	@staticmethod
	def from_dict(obj: Any) -> "PlaylistItem":
		if not isinstance(obj, dict):
			return PlaylistItem()
		track_obj = None
		trk = obj.get("track")
		if isinstance(trk, dict):
			if trk.get("duration_ms") is not None:
				track_obj = TrackModel.from_dict(trk)
			else:
				track_obj = TrackStub.from_dict(trk)
		return PlaylistItem(
			added_at=_str(obj.get("added_at")),
			added_by=UserMini.from_dict(obj.get("added_by", {})),
			is_local=bool(obj.get("is_local", False)),
			track=track_obj,
			item_id=_str(obj.get("item_id")),
		)

	def to_dict(self) -> Dict[str, Any]:
		out = {
			"added_at": self.added_at,
			"added_by": self.added_by.to_dict(),
			"is_local": self.is_local,
			"track": self.track.to_dict() if hasattr(self.track, 'to_dict') and self.track else None,
			"item_id": self.item_id,
		}
		return {k: v for k, v in out.items() if v not in (None, {}, [], "")}


@dataclass
class TracksPage:
	offset: int = 0
	total: int = 0
	items: List[PlaylistItem] = field(default_factory=list)

	@staticmethod
	def from_dict(obj: Any) -> "TracksPage":
		if not isinstance(obj, dict):
			return TracksPage(items=[])
		items: List[PlaylistItem] = []
		for it in obj.get("items", []) or []:
			items.append(PlaylistItem.from_dict(it))
		return TracksPage(
			offset=_int(obj.get("offset")) or 0,
			total=_int(obj.get("total")) or len(items),
			items=items,
		)

	def to_dict(self) -> Dict[str, Any]:
		return {
			"offset": self.offset,
			"total": self.total,
			"items": [it.to_dict() for it in (self.items or [])]
		}


@dataclass
class Owner:
	id: Optional[str] = None
	type: str = "user"
	uri: Optional[str] = None
	display_name: Optional[str] = None
	external_urls: ExternalUrls = field(default_factory=ExternalUrls)

	@staticmethod
	def from_dict(obj: Any) -> "Owner":
		if not isinstance(obj, dict):
			return Owner()
		return Owner(
			id=_str(obj.get("id")),
			type=_str(obj.get("type")) or "user",
			uri=_str(obj.get("uri")),
			display_name=_str(obj.get("display_name")),
			external_urls=ExternalUrls.from_dict(obj.get("external_urls", {})),
		)

	def to_dict(self) -> Dict[str, Any]:
		out = {
			"id": self.id,
			"type": self.type,
			"uri": self.uri,
			"display_name": self.display_name,
			"external_urls": self.external_urls.to_dict(),
		}
		return {k: v for k, v in out.items() if v not in (None, {}, [], "")}


@dataclass
class Playlist:
	name: Optional[str] = None
	description: Optional[str] = None
	collaborative: Optional[bool] = None
	images: Optional[List[Image]] = None
	owner: Owner = field(default_factory=Owner)
	snapshot_id: Optional[str] = None
	tracks: TracksPage = field(default_factory=lambda: TracksPage(items=[]))
	type: str = "playlist"

	@staticmethod
	def from_dict(obj: Any) -> "Playlist":
		if not isinstance(obj, dict):
			return Playlist(tracks=TracksPage(items=[]))
		imgs: List[Image] = []
		for im in obj.get("images", []) or []:
			im_obj = Image.from_dict(im)
			if im_obj:
				imgs.append(im_obj)
		return Playlist(
			name=_str(obj.get("name")),
			description=_str(obj.get("description")),
			collaborative=bool(obj.get("collaborative")) if obj.get("collaborative") is not None else None,
			images=imgs or None,
			owner=Owner.from_dict(obj.get("owner", {})),
			snapshot_id=_str(obj.get("snapshot_id")),
			tracks=TracksPage.from_dict(obj.get("tracks", {})),
			type=_str(obj.get("type")) or "playlist",
		)

	def to_dict(self) -> Dict[str, Any]:
		out = {
			"name": self.name,
			"description": self.description,
			"collaborative": self.collaborative,
			"images": [im.to_dict() for im in (self.images or [])],
			"owner": self.owner.to_dict(),
			"snapshot_id": self.snapshot_id,
			"tracks": self.tracks.to_dict(),
			"type": self.type,
		}
		return {k: v for k, v in out.items() if v not in (None, {}, [], "")} 