"""MHLP Writer — Write playlist list chunks for iTunesDB.

MHLP (playlist list) wraps all MHYP (playlist) chunks and provides
the playlist count in its header. Every iTunesDB needs at least a
"master playlist" referencing all tracks.

Header layout (MHLP_HEADER_SIZE = 92 bytes):
    +0x00: 'mhlp' magic (4B)
    +0x04: header_length (4B)
    +0x08: playlist_count (4B)

Supports:
- Master + user playlists (write_mhlp_with_playlists)
- Dataset 3 podcast playlists (podcast clone of dataset 2)
- Dataset 5 smart playlists (write_mhlp_smart)

Cross-referenced against:
  - iTunesDB_Parser/mhlp_parser.py
  - libgpod itdb_itunesdb.c: mk_mhlp()
"""

from __future__ import annotations

import logging
import struct
from typing import List, Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from .mhit_writer import TrackInfo
    from .mhyp_writer import PlaylistInfo

from .mhyp_writer import write_master_playlist, write_playlist

logger = logging.getLogger(__name__)


# MHLP header size - libgpod uses 92 bytes
MHLP_HEADER_SIZE = 92


def write_mhlp_empty() -> bytes:
    """
    Write an empty MHLP (playlist list) chunk.

    Note: An empty MHLP means NO playlists, which may cause issues
    on some iPods. Use write_mhlp_with_playlists() for a valid database.

    Returns:
        MHLP header with 0 playlists
    """
    header = bytearray(MHLP_HEADER_SIZE)

    # Magic
    header[0:4] = b'mhlp'

    # Header length
    struct.pack_into('<I', header, 4, MHLP_HEADER_SIZE)

    # Playlist count = 0
    struct.pack_into('<I', header, 8, 0)

    return bytes(header)


def write_mhlp(playlist_chunks: List[bytes]) -> bytes:
    """
    Write a MHLP chunk with playlists.

    Args:
        playlist_chunks: List of MHYP (playlist) chunks

    Returns:
        Complete MHLP chunk
    """
    # Concatenate all playlist data
    playlists_data = b''.join(playlist_chunks)

    header = bytearray(MHLP_HEADER_SIZE)

    # Magic
    header[0:4] = b'mhlp'

    # Header length
    struct.pack_into('<I', header, 4, MHLP_HEADER_SIZE)

    # Playlist count
    struct.pack_into('<I', header, 8, len(playlist_chunks))

    return bytes(header) + playlists_data


def write_mhlp_with_playlists(
    track_ids: List[int],
    playlists: List[PlaylistInfo],
    id_0x24,
    tracks: Optional[List[TrackInfo]] = None,
    capabilities=None,
    master_playlist_name: str = "iPod",
) -> bytes:
    """
    Write an MHLP chunk with the master playlist + user playlists.

    The master playlist is always first, followed by regular/smart playlists.
    This is used for MHSD type 2 (playlists dataset).

    The master playlist is auto-generated from the full track list; its
    display name is controlled by *master_playlist_name*.  The *playlists*
    list should contain only user playlists (no master).

    Args:
        track_ids: List of ALL track IDs in the database (for master playlist)
        playlists: List of user PlaylistInfo objects (master is NOT included)
        tracks: List of ALL TrackInfo objects (needed for library indices)
        id_0x24: Database-wide ID from MHBD offset 0x24
        capabilities: Optional DeviceCapabilities for video sort indices.
        master_playlist_name: Display name for the auto-generated master playlist.

    Returns:
        Complete MHLP chunk
    """
    chunks = []

    # Master playlist MUST be first
    master = write_master_playlist(
        track_ids, tracks=tracks, id_0x24=id_0x24,
        capabilities=capabilities, name=master_playlist_name,
    )
    chunks.append(master)

    # Sanity: strip rogue master flags from dataset-2 user playlists.
    # Only the auto-generated master above should have master=True.
    # Dataset 5 built-in categories (mhsd5_type != 0) legitimately
    # need master=True, so we never touch those — even if they end up
    # here by accident.
    for p in playlists:
        if p.master and not p.mhsd5_type:
            logger.warning(
                "Stripped master flag from user playlist '%s' — "
                "master is auto-generated for dataset 2",
                p.name,
            )
            p.master = False

    # Write all user playlists (regular and smart).
    for pl in playlists:
        chunks.append(write_playlist(pl, id_0x24=id_0x24))

    return write_mhlp(chunks)


def write_mhlp_smart(
    playlists: List[PlaylistInfo],
    id_0x24: int = 0,
) -> bytes:
    """
    Write an MHLP chunk for dataset type 5 (smart playlist list).

    These playlists define iPod built-in browse categories (Music, Movies,
    TV Shows, Audiobooks, Podcasts, Rentals). Each has a mhsd5_type value
    and smart rules that filter by media type.

    **Master flag semantics for dataset 5:**
    All built-in categories legitimately have ``master=True`` which writes
    ``type=1`` at MHYP offset +0x14.  This is the SAME byte used by the
    master playlist in dataset 2, but the meaning differs:

    - Dataset 2 ``type=1``: "this is the master playlist" (exactly one)
    - Dataset 5 ``type=1``: "this is a built-in system category" (all of them)

    No single-master constraint is enforced here — every ds5 category
    needs ``master=True`` for the iPod firmware to recognise it.

    Args:
        playlists: List of PlaylistInfo objects (smart playlists only)
        id_0x24: Database-wide ID from MHBD offset 0x24

    Returns:
        Complete MHLP chunk, or empty MHLP if no smart playlists
    """
    if not playlists:
        return write_mhlp_empty()

    chunks = []
    for pl in playlists:
        chunks.append(write_playlist(pl, id_0x24=id_0x24))

    return write_mhlp(chunks)
