pub(crate) mod detail;
pub(crate) mod library;
pub(crate) mod season_detail;

use crate::database::Database;
use crate::tmdb_service::{self, ShowMatchData};
use crate::ui::shared::tmdb_matcher::{self, Backend, MatcherState, Message as TmdbMessage};
use iced::Task;

const TMDB_BACKEND: Backend<ShowMatchData> = Backend {
    search: tmdb_service::search_shows,
    fetch_match: tmdb_service::fetch_show_match,
};

#[derive(Debug, Clone)]
pub(crate) enum Message {
    LibraryScrolled(iced::widget::scrollable::Viewport),
    Tmdb(TmdbMessage<ShowMatchData>),
}

/// Side effects the root reacts to.
#[derive(Debug, Clone, Copy)]
pub(crate) enum Event {
    MatchApplied,
}

/// State owned by the show detail screen (`Screen::ShowDetail`).
pub(crate) struct DetailState {
    pub(crate) show_id: i64,
    pub(crate) tmdb: MatcherState,
}

impl DetailState {
    pub(crate) fn new(item: &library::ShowLibraryItem) -> Self {
        Self {
            show_id: item.id,
            tmdb: MatcherState::new(
                &item.title,
                item.tmdb_id.is_some(),
                "Search TV show...",
                "Matching... Fetching show details, seasons, and episodes.",
            ),
        }
    }
}

pub(crate) struct State {
    pub(crate) items: Vec<library::ShowLibraryItem>,
    pub(crate) seasons: Vec<detail::SeasonItem>,
    pub(crate) season_episodes: Vec<season_detail::EpisodeItem>,
    pub(crate) scroll_offset: f32,
    pub(crate) viewport_height: f32,
}

impl State {
    pub(crate) fn new(items: Vec<library::ShowLibraryItem>) -> Self {
        Self {
            items,
            seasons: Vec::new(),
            season_episodes: Vec::new(),
            scroll_offset: 0.0,
            viewport_height: 0.0,
        }
    }
}

pub(crate) fn update(
    state: &mut State,
    detail: Option<&mut DetailState>,
    msg: Message,
    db: &mut Database,
    tmdb_token: &Option<String>,
) -> (Task<crate::ui::Message>, Option<Event>) {
    match msg {
        Message::LibraryScrolled(viewport) => {
            state.scroll_offset = viewport.absolute_offset().y;
            state.viewport_height = viewport.bounds().height;
            (Task::none(), None)
        }
        Message::Tmdb(TmdbMessage::MatchCompleted(show_id, result)) => {
            // Applied via the id embedded in the message, so the match lands
            // on the right show even if the user navigated away while the
            // request was in flight.
            match result {
                Ok(data) => {
                    match state.items.iter_mut().find(|s| s.id == show_id) {
                        Some(item) => match tmdb_service::apply_show_match(db, show_id, &data) {
                            Ok(applied) => {
                                item.tmdb_id = Some(data.tmdb_id as i64);
                                item.title = data.original_name.clone();
                                if let Some(p) = applied.cover {
                                    item.cover_path = Some(p);
                                }
                                if let Some(p) = applied.backdrop {
                                    item.backdrop_path = Some(p);
                                }
                            }
                            Err(e) => log::error!("failed to apply show tmdb match: {e}"),
                        },
                        None => log::warn!("show {show_id} vanished before TMDB match applied"),
                    }
                    if let Some(d) = detail
                        && d.show_id == show_id
                    {
                        d.tmdb.match_succeeded();
                    }
                    (Task::none(), Some(Event::MatchApplied))
                }
                Err(e) => {
                    if let Some(d) = detail
                        && d.show_id == show_id
                    {
                        d.tmdb.match_failed(e);
                    }
                    (Task::none(), None)
                }
            }
        }
        // Delegate the search flow to the shared matcher
        Message::Tmdb(m) => match detail {
            Some(d) => (
                tmdb_matcher::update(&mut d.tmdb, m, tmdb_token, d.show_id, TMDB_BACKEND)
                    .map(|m| crate::ui::Message::Show(Message::Tmdb(m))),
                None,
            ),
            None => (Task::none(), None),
        },
    }
}
