import os
import re
from copy import copy
from datetime import date, datetime, timedelta
from urllib.parse import quote

from lib.clients.tmdb.utils.utils import tmdb_get
from lib.db.pickle_db import PickleDatabase
from lib.jacktook.utils import kodilog
from lib.utils.general.utils import (
    execute_thread_pool,
    set_media_infoTag,
    set_pluging_category,
    translate_weekday,
)
from lib.utils.kodi.utils import (
    ADDON_PATH,
    add_directory_items_batch,
    apply_section_view,
    build_url,
    end_of_directory,
    get_setting,
    kodi_play_media,
    make_list_item,
    translation,
)


def show_weekly_calendar(library=False):
    set_pluging_category(translation(90021))

    if library:
        source_dict = PickleDatabase().get_key("jt:lib")
    else:
        source_dict = PickleDatabase().get_key("jt:lth")

    tv_shows = [(title, data) for title, data in source_dict.items() if data.get("mode") == "tv"]

    results = []

    # Broadcast Day Adjustment logic
    # This is a manual simulation of broadcast times, not real timezone handling.
    use_broadcast_adjustment = get_setting("use_broadcast_adjustment", False)
    broadcast_offset = int(get_setting("broadcast_offset", 0))

    def calculate_adjusted_broadcast(date_str, offset_hours):
        """
        Calculates an adjusted date and time based on a manual hour offset.

        Assumes the original air time is midnight (00:00).
        """
        if not date_str:

            return None, None

        try:
            # Parse date, assume midnight (00:00) for calculation
            dt = datetime.strptime(date_str, "%Y-%m-%d")

            if use_broadcast_adjustment:
                # Clamp offset to -24 to +24 (defensive, though settings limit this)
                offset = max(-24, min(24, offset_hours))
                dt += timedelta(hours=offset)

            return dt.strftime("%Y-%m-%d"), dt.strftime("%H:%M")
        except Exception as e:
            kodilog(f"Error calculating broadcast adjustment: {e}")
            return date_str, "00:00"

    def fetch_episodes_for_show(item):
        title, data = item
        episodes, details = get_episodes_for_show(data.get("ids"))
        for ep in episodes:
            # create a shallow copy to avoid mutating the cached data
            ep_copy = copy(ep)

            original_air_date = ep_copy.get("air_date")
            adjusted_date, adjusted_time = calculate_adjusted_broadcast(
                original_air_date, broadcast_offset
            )

            # Attach adjusted values for display
            ep_copy["air_date"] = adjusted_date
            ep_copy["air_time"] = adjusted_time

            if adjusted_date and is_this_week(adjusted_date):
                results.append((title, data, ep_copy, details))

    # Use thread pool to fetch episodes in parallel
    execute_thread_pool(tv_shows, fetch_episodes_for_show)

    # Add fixed item showing current date at the top
    current_date = datetime.now().strftime("%A, %d %B %Y")
    directory_items = []

    date_item = make_list_item(
        label=f"[UPPERCASE][COLOR=orange]Today: {current_date}[/COLOR][/UPPERCASE]"
    )
    date_item.setArt({"icon": os.path.join(ADDON_PATH, "resources", "img", "history.png")})
    directory_items.append(("", date_item, False))

    today_str = datetime.now().strftime("%Y-%m-%d")

    results_today = [r for r in results if r[2].get("air_date") == today_str]
    results_other = [r for r in results if r[2].get("air_date") != today_str]

    results = sorted(results_today, key=lambda x: x[2].get("air_date", "")) + sorted(
        results_other, key=lambda x: x[2].get("air_date", "")
    )

    # Add items to Kodi UI
    for (
        title,
        data,
        ep,
        details,
    ) in results:
        # Get episode name if available
        ep_name = ep.get("name", "")

        # Build complete IDs if possible from show details
        ids = copy(data.get("ids", {}))
        external_ids = getattr(details, "external_ids", {})
        if external_ids:
            if external_ids.get("imdb_id"):
                ids["imdb_id"] = external_ids.get("imdb_id")
            if external_ids.get("tvdb_id"):
                ids["tvdb_id"] = external_ids.get("tvdb_id")

        tv_data = {
            "name": quote(ep_name or title),
            "episode": ep["number"],
            "season": ep["season"],
        }

        # Get the day of the week from air_date
        air_date_obj = parse_date_str(ep["air_date"])
        weekday_name = air_date_obj.strftime("%A")
        weekday_name_translated = translate_weekday(weekday_name)

        # Mark if episode is released today
        is_today = ep["air_date"] == today_str
        mark = "[UPPERCASE][COLOR=orange]TODAY- [/COLOR][/UPPERCASE]" if is_today else ""

        ep_title = f"{mark}{weekday_name_translated} - ({ep['air_date']} {ep.get('air_time', '00:00')}) - {title} - S{ep['season']:02}E{ep['number']:02}"

        list_item = make_list_item(label=ep_title)
        list_item.setProperty("IsPlayable", "true")

        set_media_infoTag(list_item, data=details, mode=data.get("mode"))

        # Add context menu items
        list_item.addContextMenuItems(
            [
                (
                    translation(90049),
                    kodi_play_media(
                        name="search",
                        mode=data.get("mode"),
                        query=title,
                        ids=ids,
                        tv_data=tv_data,
                        rescrape=True,
                    ),
                ),
            ]
        )

        directory_items.append(
            (
                build_url(
                    "search",
                    mode=data.get("mode"),
                    media_type=data.get("mode"),
                    query=title,
                    ids=ids,
                    tv_data=tv_data,
                ),
                list_item,
                False,
            )
        )

    add_directory_items_batch(directory_items)

    end_of_directory(cache=False)
    if library:
        apply_section_view("view.library")
    else:
        apply_section_view("view.history")


def get_episodes_for_show(ids):
    tmdb_id = ids.get("tmdb_id")

    try:
        show_details = tmdb_get("tv_details", tmdb_id)
        seasons = show_details.seasons
        seasons = [s for s in seasons if s.get("season_number", 0) > 0]
        if not seasons:
            return [], show_details
        latest_season = max(seasons, key=lambda s: s.get("season_number", 0))
        season_number = latest_season.get("season_number")
        season_details = tmdb_get("season_details", {"id": tmdb_id, "season": season_number})

        episodes = []
        for ep in season_details.episodes:
            air_date = ep.get("air_date")
            if air_date:
                episodes.append(
                    {
                        "season": season_number,
                        "number": ep.get("episode_number"),
                        "air_date": air_date,
                        "name": ep.get("name"),
                    }
                )
        return episodes, show_details
    except Exception as e:
        kodilog(f"Error fetching episodes for TMDB ID {tmdb_id}: {e}")
        return {}, []


def is_this_week(date_str):
    """Check if the date_str (YYYY-MM-DD) is in the current week."""
    try:

        d = parse_date_str(date_str)
        today = date.today()
        year, week, _ = today.isocalendar()
        d_year, d_week, _ = d.isocalendar()
        return (d_year, d_week) == (year, week)
    except Exception as e:
        kodilog(f"Error: {e!s}")


def parse_date_str(date_str: str) -> date:
    """Parse a date string (YYYY-MM-DD or similar) into a date object without using datetime.strptime."""
    date_str_cleaned = date_str.strip().split("T")[0]

    match = re.search(r"(\d{4})[\-\/]?(\d{1,2})[\-\/]?(\d{1,2})", date_str_cleaned)
    if match:
        year, month, day = map(int, match.groups())
        return date(year, month, day)
    else:
        raise ValueError(f"Invalid date format: {date_str}")
