import json
from http.cookies import SimpleCookie
from pathlib import Path
from typing import Optional

from fuo_ytmusic.consts import HEADER_FILE

# Store selected profile in headerfile using a custom key; this is not part of
# YouTube headers but lets us persist profile selection without extra files.
PROFILE_GAIA_KEY = "X-Feeluown-Profile-Gaia-Id"
# Include both root YouTube domain and Music subdomain explicitly to avoid
# extractor differences when matching cookie scope.
YTDLP_COOKIE_DOMAINS = (".youtube.com", ".music.youtube.com", ".google.com")

# yt-dlp reads cookies from a Netscape cookiefile. We persist this file next to
# ytmusic_header.json and keep it synchronized whenever header auth cookie is
# written/updated, so playback can reuse the same long-lived auth lifecycle.


class YtdlpCookiefileManager:
    """Manage yt-dlp cookiefile generation for a specific headerfile path."""

    def __init__(self, headerfile_path=HEADER_FILE):
        self._headerfile_path = (
            Path(headerfile_path) if headerfile_path is not None else None
        )

    @property
    def headerfile_path(self) -> Optional[Path]:
        return self._headerfile_path

    @property
    def cookiefile_path(self) -> Optional[Path]:
        path = self._headerfile_path
        if path is None or not path.exists():
            return None
        if path.suffix:
            return path.with_suffix(".cookies.txt")
        return Path(f"{path}.cookies.txt")

    def write(self, cookie_value: str) -> Optional[Path]:
        # Write/update the persistent cookiefile used by yt-dlp. Empty cookie
        # means delete the file so stale auth is not reused.
        cookiefile_path = self.cookiefile_path
        if cookiefile_path is None:
            return None

        cookie_jar = SimpleCookie()
        cookie_jar.load(cookie_value or "")
        if not cookie_jar:
            if cookiefile_path.exists():
                cookiefile_path.unlink()
            return None

        lines = [
            "# Netscape HTTP Cookie File",
            "# This file is generated by FeelUOwn ytmusic plugin. Do not edit.",
            "",
        ]
        for morsel in cookie_jar.values():
            for domain in YTDLP_COOKIE_DOMAINS:
                lines.append(
                    f"{domain}\tTRUE\t/\tTRUE\t0\t{morsel.key}\t{morsel.value}"
                )

        cookiefile_path.parent.mkdir(parents=True, exist_ok=True)
        with cookiefile_path.open("w", encoding="utf-8") as handle:
            handle.write("\n".join(lines))
            handle.write("\n")
        return cookiefile_path


def write_headerfile(auth: str, cookie: str, headerfile_path=HEADER_FILE) -> None:
    tpl = {
        "Accept": "*/*",
        "Authorization": auth,
        "Content-Type": "application/json",
        "X-Goog-AuthUser": "0",
        "x-origin": "https://music.youtube.com",
        "Cookie": cookie,
    }
    with headerfile_path.open("w") as handle:
        json.dump(tpl, handle, indent=2, ensure_ascii=True)
    # Keep yt-dlp cookiefile in sync with freshly written headerfile auth.
    YtdlpCookiefileManager(headerfile_path).write(cookie)


def read_headerfile(headerfile_path=HEADER_FILE) -> dict:
    if headerfile_path is None:
        return {}
    try:
        if headerfile_path.exists():
            with open(headerfile_path, "r") as handle:
                data = json.load(handle)
            return data if isinstance(data, dict) else {}
    except Exception:
        return {}
    return {}


def strip_profile_meta(headers: dict) -> dict:
    if not headers:
        return {}
    # Ensure our custom profile marker isn't forwarded as a real request header.
    data = dict(headers)
    data.pop(PROFILE_GAIA_KEY, None)
    return data


def get_profile_gaia_id(headerfile_path=HEADER_FILE) -> Optional[str]:
    # Read persisted profile selection from headerfile (if any).
    data = read_headerfile(headerfile_path)
    return data.get(PROFILE_GAIA_KEY)


def update_profile_gaia_id(gaia_id: Optional[str], headerfile_path=HEADER_FILE) -> bool:
    if headerfile_path is None:
        return False
    # Persist/clear selected profile in headerfile so it survives app restarts.
    data = read_headerfile(headerfile_path)
    original = data.get(PROFILE_GAIA_KEY)
    if gaia_id:
        data[PROFILE_GAIA_KEY] = gaia_id
    else:
        data.pop(PROFILE_GAIA_KEY, None)
    if data.get(PROFILE_GAIA_KEY) == original:
        return False
    with open(headerfile_path, "w") as handle:
        json.dump(data, handle, indent=2, ensure_ascii=True)
    # Profile switch metadata does not change auth cookie, so no cookiefile sync.
    return True


def get_ytdlp_cookiefile_path(headerfile_path=HEADER_FILE) -> Optional[Path]:
    # Backward-compatible helper. New code should use YtdlpCookiefileManager.
    return YtdlpCookiefileManager(headerfile_path).cookiefile_path


def write_ytdlp_cookiefile(
    cookie_value: str, headerfile_path=HEADER_FILE
) -> Optional[Path]:
    # Backward-compatible helper. New code should use YtdlpCookiefileManager.
    return YtdlpCookiefileManager(headerfile_path).write(cookie_value)


def update_headerfile_cookie(cookie_value: str, headerfile_path=HEADER_FILE) -> bool:
    if headerfile_path is None:
        return False
    try:
        if headerfile_path.exists():
            with open(headerfile_path, "r") as handle:
                data = json.load(handle)
        else:
            data = {}
    except Exception:
        data = {}

    changed = data.get("Cookie") != cookie_value
    if changed:
        data["Cookie"] = cookie_value
        with open(headerfile_path, "w") as handle:
            json.dump(data, handle, indent=2, ensure_ascii=True)

    # Keep yt-dlp cookiefile lifecycle aligned with header cookie updates.
    YtdlpCookiefileManager(headerfile_path).write(cookie_value)
    return changed
