"""Media index tab UI."""

from __future__ import annotations

from pathlib import PurePosixPath
from typing import Any, Callable

import pandas as pd
import streamlit as st
from st_aggrid import AgGrid, DataReturnMode, GridOptionsBuilder, GridUpdateMode, JsCode

from media_library_viewer.services.media_index import MediaIndex, build_media_index


def aggrid_selected_rows(response: dict[str, Any]) -> list[dict[str, Any]]:
    """Return selected rows from a streamlit-aggrid response."""
    selected_rows = response.get("selected_rows")
    if selected_rows is None:
        return []
    if isinstance(selected_rows, pd.DataFrame):
        return selected_rows.to_dict("records")
    return list(selected_rows)


def format_elapsed(seconds: float | int | None) -> str:
    if seconds is None:
        return ""
    seconds = float(seconds)
    if seconds < 60:
        return f"{seconds:.1f}s"
    minutes = int(seconds // 60)
    remainder = seconds % 60
    if minutes < 60:
        return f"{minutes}m {remainder:.0f}s"
    hours = minutes // 60
    minutes = minutes % 60
    return f"{hours}h {minutes}m"


def render_media_tab(
    client,
    user_id: str,
    libraries: list[dict[str, Any]],
    set_file_browser_path: Callable[[str, str | None, bool], None],
) -> None:
    """Render the SQLite-backed media inventory tab."""
    st.subheader("Media inventory")
    st.caption("SQLite-backed index for full-library sorting/filtering. File size/bitrate/HDR are based on Jellyfin media source metadata, not a full ffprobe scan.")

    index = MediaIndex()
    status = index.status()

    status_col, build_col, refresh_col = st.columns([3.5, 1.2, 1])
    if status.exists:
        status_parts = [f"Index: {status.item_count:,} items"]
        if status.updated_at_label:
            status_parts.append(f"updated {status.updated_at_label}")
        if status.build_duration_seconds is not None:
            status_parts.append(f"last build took {format_elapsed(status.build_duration_seconds)}")
        status_col.caption(" | ".join(status_parts))
    else:
        status_col.warning("No local media index yet. Build it to enable full-library sorting and filtering.")

    if build_col.button("Build index", key="media_index_build", use_container_width=True):
        with st.spinner("Building media index from Jellyfin. This can take a while for large libraries..."):
            count = build_media_index(client, user_id, libraries, index)
        st.success(f"Indexed {count:,} media items.")
        st.rerun()
    if refresh_col.button("Refresh", key="media_index_refresh", use_container_width=True):
        st.rerun()

    if not index.status().exists:
        st.info("The Media tab uses a local SQLite index so sorting by size, bitrate, HDR, codec, season, and episode works across the whole library rather than just the current Jellyfin page.")
        return

    library_options = {lib["Name"]: lib["Id"] for lib in libraries}

    filter_col, type_col, search_col, page_size_col, page_col = st.columns([1.9, 1.8, 2.4, 1.1, 1])
    selected_libraries = filter_col.multiselect(
        "Libraries",
        list(library_options.keys()),
        default=list(library_options.keys()),
        key="media_inventory_libraries",
    )
    media_types = type_col.multiselect(
        "Types",
        ["Movie", "Episode", "Video"],
        default=["Movie", "Episode"],
        key="media_inventory_types",
    )
    search = search_col.text_input("Search", key="media_inventory_search")
    page_size = page_size_col.selectbox("Rows", [50, 100, 250, 500], index=1, key="media_inventory_page_size")
    page = page_col.number_input("Page", min_value=1, value=1, step=1, key="media_inventory_page")

    sort_options = {
        "Title": "title",
        "Series": "series",
        "Season": "season",
        "Episode": "episode",
        "Type": "type",
        "Year": "year",
        "Runtime": "runtime",
        "Size": "size",
        "Bitrate": "bitrate",
        "HDR": "hdr",
        "Video codec": "video",
        "Resolution": "resolution",
        "Date added": "date_added",
        "Library": "library",
        "Path": "path",
    }
    sort_col, order_col, hdr_col = st.columns([1.4, 1.1, 1.2])
    sort_label = sort_col.selectbox("Sort", list(sort_options.keys()), key="media_inventory_sort")
    sort_order_label = order_col.selectbox("Order", ["Ascending", "Descending"], key="media_inventory_sort_order")
    hdr_filter = hdr_col.selectbox("HDR filter", ["All", "HDR only", "SDR/unknown only"], key="media_inventory_hdr_filter")

    if not selected_libraries:
        st.info("Select at least one library to show indexed media.")
        return

    rows, total = index.query(
        library_ids=[library_options[name] for name in selected_libraries],
        media_types=media_types or ["Movie", "Episode", "Video"],
        search=search,
        hdr_filter=hdr_filter,
        sort_key=sort_options[sort_label],
        sort_order=sort_order_label,
        limit=int(page_size),
        offset=(int(page) - 1) * int(page_size),
    )

    st.caption(f"Showing {len(rows)} of {total:,} indexed matching items. Sort and filters apply to the full local index.")

    columns = [
        "title", "series", "season", "episode", "type", "year", "runtime_min",
        "size", "bitrate", "hdr", "video", "resolution", "date_added", "library", "path", "id",
    ]
    if not rows:
        st.info("No media found for the current filters.")
        return

    table_df = pd.DataFrame(rows)[columns].fillna("")
    selected_media_path = st.session_state.get("media_inventory_selected_path")

    grid_builder = GridOptionsBuilder.from_dataframe(table_df)
    grid_builder.configure_default_column(editable=False, resizable=True, sortable=False, filter=False, autoSize=True)
    grid_builder.configure_column("title", header_name="Title", minWidth=150)
    grid_builder.configure_column("series", header_name="Series", minWidth=120)
    grid_builder.configure_column("season", header_name="Season", maxWidth=95)
    grid_builder.configure_column("episode", header_name="Episode", maxWidth=105)
    grid_builder.configure_column("type", header_name="Type", maxWidth=100)
    grid_builder.configure_column("year", header_name="Year", maxWidth=90)
    grid_builder.configure_column("runtime_min", header_name="Runtime (min)", maxWidth=125)
    grid_builder.configure_column("size", header_name="Size", maxWidth=120)
    grid_builder.configure_column("bitrate", header_name="Bitrate", maxWidth=125)
    grid_builder.configure_column("hdr", header_name="HDR", maxWidth=80)
    grid_builder.configure_column("video", header_name="Video codec", maxWidth=120)
    grid_builder.configure_column("resolution", header_name="Resolution", maxWidth=120)
    grid_builder.configure_column("date_added", header_name="Date added", maxWidth=120)
    grid_builder.configure_column("library", header_name="Library", maxWidth=140)
    grid_builder.configure_column("path", header_name="Path", minWidth=200)
    grid_builder.configure_column("id", hide=True)
    grid_builder.configure_selection(selection_mode="single", use_checkbox=False)
    grid_options = grid_builder.build()
    grid_options["autoSizeStrategy"] = {"type": "fitCellContents"}
    grid_options["rowSelection"] = {
        "mode": "singleRow",
        "checkboxes": False,
        "headerCheckbox": False,
        "enableClickSelection": True,
    }
    grid_options["suppressRowClickSelection"] = False
    grid_options["suppressCellFocus"] = True
    grid_options["onCellClicked"] = JsCode(
        """
        function(event) {
            if (event && event.node) {
                event.node.setSelected(true, true);
            }
        }
        """
    )
    grid_response = AgGrid(
        table_df,
        gridOptions=grid_options,
        height=min(650, 35 * (len(rows) + 1)),
        fit_columns_on_grid_load=True,
        data_return_mode=DataReturnMode.AS_INPUT,
        update_mode=GridUpdateMode.SELECTION_CHANGED,
        key="media_inventory_grid",
        theme="streamlit",
        allow_unsafe_jscode=True,
    )
    selected_rows = aggrid_selected_rows(grid_response)
    if selected_rows:
        selected_media_path = selected_rows[0].get("path")
        if selected_media_path:
            st.session_state["media_inventory_selected_path"] = selected_media_path
            # Auto-sync File browser location from Media row selection.
            # Guarded by last-synced path to avoid reapplying on every rerun.
            last_synced = st.session_state.get("media_inventory_last_synced_path")
            if selected_media_path != last_synced:
                set_file_browser_path(str(PurePosixPath(selected_media_path).parent), selected_media_path)
                st.session_state["media_inventory_last_synced_path"] = selected_media_path

    if selected_media_path:
        st.caption(f"Selected media path: `{selected_media_path}` (File browser folder synced automatically)")
    else:
        st.caption("Select a table row to automatically sync its containing folder to the File browser tab.")

    with st.expander("Notes"):
        st.write(
            "The Media tab now queries a local SQLite index, so sorting/filtering applies across the indexed library. "
            "Rebuild the index after Jellyfin scans or metadata changes. Full ffprobe enrichment for every item can be added later as a background index extension."
        )
