mod home;
mod movie;
mod player;
mod shared;
mod show;

use crate::database::entities::{MovieWithCover, RecentSeasonWithCover, ShowWithCover};
use crate::database::{Database, queries};
use crate::player::{FrameAction, Player, PlayerState, error::PlayerError};
use crate::scanner_task::{self, ScanEvent, ScanHandle, ScanReceiver};
use iced::futures::SinkExt;
use iced::{Element, Task};
use player::AudioStreamOption;
use rusqlite::params;
use shared::nav::{BreadcrumbNav, Tab};
use shared::screensaver::ScreensaverInhibitor;
use show::detail::SeasonItem;
use show::library::ShowLibraryItem;
use show::season_detail::EpisodeItem;
use std::{
    path::{Path, PathBuf},
    sync::Mutex,
    time::Instant,
};
use thiserror::Error;

fn images_cache_dir() -> PathBuf {
    dirs::cache_dir()
        .unwrap_or_else(|| PathBuf::from(".cache"))
        .join("myplayer")
        .join("images")
}

fn cache_image_path(cache_dir: &Path, filename: Option<String>) -> Option<PathBuf> {
    filename.map(|f| cache_dir.join(f.trim_start_matches('/')))
}

fn build_movie_library_item(row: MovieWithCover, cache_dir: &Path) -> movie::library::LibraryItem {
    let abs_file = PathBuf::from(&row.library_root_path)
        .join(&row.file)
        .to_string_lossy()
        .to_string();
    movie::library::LibraryItem {
        id: row.id,
        tmdb_id: row.tmdb_id,
        title: row.original_title.unwrap_or(row.title),
        release_year: row.release_year,
        file: abs_file,
        cover_path: cache_image_path(cache_dir, row.cover_filename),
        backdrop_path: cache_image_path(cache_dir, row.backdrop_filename),
        description: row.description,
        rating: row.rating,
        watched: row.watched,
        playback_position: row.playback_position,
    }
}

fn build_show_library_item(row: ShowWithCover, cache_dir: &Path) -> ShowLibraryItem {
    ShowLibraryItem {
        id: row.id,
        tmdb_id: row.tmdb_id,
        title: row.original_title.unwrap_or(row.title),
        release_year: row.release_year,
        cover_path: cache_image_path(cache_dir, row.cover_filename),
        backdrop_path: cache_image_path(cache_dir, row.backdrop_filename),
        description: row.description,
        rating: row.rating,
        watched: row.watched,
    }
}

fn build_recent_season_item(
    row: RecentSeasonWithCover,
    cache_dir: &Path,
) -> home::RecentSeasonItem {
    home::RecentSeasonItem {
        show_id: row.show_id,
        show_title: row.show_title,
        season_number: row.season_number,
        title: row.title,
        cover_path: cache_image_path(cache_dir, row.cover_filename),
        watched: row.watched,
    }
}

#[derive(Debug, Error)]
pub enum AppError {
    #[error("iced error: {0}")]
    Iced(#[from] iced::Error),
    #[error("player error: {0}")]
    Player(#[from] PlayerError),
}

pub fn run(player: Option<Player>, db: Database, log_path: PathBuf) -> Result<(), AppError> {
    let window_size = match &player {
        Some(p) => iced::Size::new(
            p.media_info().width as f32,
            p.media_info().height as f32 + 36.0,
        ),
        None => iced::Size::new(1280.0, 720.0),
    };
    let boot_data = Mutex::new(Some((player, db, log_path)));

    iced::application(
        move || {
            let (player, db, log_path) = boot_data.lock().unwrap().take().unwrap();
            IcedApp::init(player, db, log_path)
        },
        IcedApp::update,
        IcedApp::view,
    )
    .title("myplayer")
    .subscription(IcedApp::subscription)
    .window(iced::window::Settings {
        size: window_size,
        maximized: true,
        ..Default::default()
    })
    .run()?;

    Ok(())
}

#[derive(Debug, Clone)]
enum Message {
    Player(player::Message),
    Movie(movie::Message),
    Show(show::Message),
    Home(home::Message),
    #[allow(unused)]
    Tick(Instant),
    EscapeKey(iced::window::Id),
    MouseMoved,
    // Home cross-domain bridges
    HomeMovieClicked(usize),
    HomeSeasonClicked(usize),
    // Navigation
    SwitchTab(Tab),
    NavigateBreadcrumb(BreadcrumbNav),
    // Movie cross-domain bridges
    MovieDetail(usize),
    PlayMovie(usize),
    // TV show cross-domain bridges
    ShowDetail(usize),
    ShowSeasonDetail(usize),
    PlayEpisode(String),
    // Watched toggle (cross-screen)
    ToggleShowWatched,
    ToggleSeasonWatched,
    ToggleEpisodeWatched(usize),
    // Continue watching
    ContinueWatchingClicked(usize),
    // Search
    SearchQueryChanged(String),
    FocusSearch,
    // UI scale
    ZoomIn,
    ZoomOut,
    // Logs
    OpenLogs,
    CloseLogs,
    // Background scanner events
    StartScan,
    CancelScan,
    SpinnerTick,
    Scan(ScanEvent),
    QuitApp(iced::window::Id),
}

#[derive(Clone)]
enum ReturnScreen {
    Home,
    MovieLibrary,
    MovieDetail {
        movie_index: usize,
    },
    ShowLibrary,
    SeasonDetail {
        show_index: usize,
        season_index: usize,
    },
}

enum Screen {
    Home,
    Library,
    Detail {
        movie_index: usize,
    },
    ShowLibrary,
    ShowDetail {
        show_index: usize,
    },
    SeasonDetail {
        show_index: usize,
        season_index: usize,
    },
    Logs {
        content: String,
    },
    Player(player::State),
}

enum ScreenAnchor {
    None,
    MovieDetail { movie_id: i64 },
    ShowDetail { show_id: i64 },
    SeasonDetail { show_id: i64, season_id: i64 },
}

struct IcedApp {
    screen: Screen,
    db: Database,
    tmdb_api_token: Option<String>,
    log_path: PathBuf,
    last_mouse_move: Instant,
    return_screen: ReturnScreen,
    home: home::State,
    movie: movie::State,
    show: show::State,
    last_played_episode_file: Option<String>,
    playing_movie_index: Option<usize>,
    play_next: bool,
    search_query: String,
    ui_scale: f32,
    audio_volume: f32,
    audio_muted: bool,
    scan_handle: Option<ScanHandle>,
    spinner_frame: usize,
}

impl IcedApp {
    fn init(player: Option<Player>, mut db: Database, log_path: PathBuf) -> (Self, Task<Message>) {
        let tmdb_api_token = db.get_host("tmdb_api_key").unwrap_or(None);
        let ui_scale = db
            .get_host("ui_scale")
            .ok()
            .flatten()
            .and_then(|s| s.parse::<f32>().ok())
            .unwrap_or(1.0)
            .clamp(1.0, 3.0);
        let audio_volume = db
            .get_host("audio_volume")
            .ok()
            .flatten()
            .and_then(|s| s.parse::<f32>().ok())
            .map(|v| v.clamp(0.0, 1.0))
            .unwrap_or(1.0);
        let audio_muted = db
            .get_host("audio_muted")
            .ok()
            .flatten()
            .map(|s| s == "1")
            .unwrap_or(false);
        let cache_dir = images_cache_dir();

        let movie_items: Vec<movie::library::LibraryItem> = db
            .find_movies_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_movie_library_item(row, &cache_dir))
            .collect();

        let show_items: Vec<ShowLibraryItem> = db
            .find_shows_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_show_library_item(row, &cache_dir))
            .collect();

        let home_recent_movies: Vec<movie::library::LibraryItem> = db
            .find_recent_movies_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_movie_library_item(row, &cache_dir))
            .collect();

        let home_recent_seasons: Vec<home::RecentSeasonItem> = db
            .find_recent_seasons_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_recent_season_item(row, &cache_dir))
            .collect();

        let home_continue_watching = Self::build_continue_watching(
            db.find_continue_watching_movies().unwrap_or_default(),
            db.find_continue_watching_episodes().unwrap_or_default(),
            &cache_dir,
        );

        let screen = match player {
            Some(ctx) => Self::init_player(ctx, audio_volume, audio_muted),
            None => Screen::Home,
        };

        (
            Self {
                screen,
                db,
                tmdb_api_token,
                log_path,
                last_mouse_move: Instant::now(),
                return_screen: ReturnScreen::Home,
                home: home::State {
                    continue_watching: home_continue_watching,
                    recent_movies: home_recent_movies,
                    recent_seasons: home_recent_seasons,
                    cw_offset: 0,
                    movies_offset: 0,
                    seasons_offset: 0,
                },
                movie: {
                    let mut s = movie::State::new(movie_items);
                    s.viewport_height = 720.0;
                    s
                },
                show: {
                    let mut s = show::State::new(show_items);
                    s.viewport_height = 720.0;
                    s
                },
                last_played_episode_file: None,
                playing_movie_index: None,
                play_next: true,
                search_query: String::new(),
                ui_scale,
                audio_volume,
                audio_muted,
                scan_handle: None,
                spinner_frame: 0,
            },
            Task::none(),
        )
    }

    fn active_tab(&self) -> Tab {
        match &self.screen {
            Screen::Home => Tab::Home,
            Screen::Library | Screen::Detail { .. } => Tab::Movies,
            Screen::ShowLibrary | Screen::ShowDetail { .. } | Screen::SeasonDetail { .. } => {
                Tab::TvShows
            }
            Screen::Logs { .. } => Tab::Home,
            Screen::Player(_) => match &self.return_screen {
                ReturnScreen::MovieDetail { .. } | ReturnScreen::MovieLibrary => Tab::Movies,
                ReturnScreen::Home => Tab::Home,
                _ => Tab::TvShows,
            },
        }
    }

    fn breadcrumb_segments(&self) -> Vec<(String, Option<BreadcrumbNav>)> {
        match &self.screen {
            Screen::Home => vec![("Home".to_string(), None)],
            Screen::Library => vec![("Movies".to_string(), None)],
            Screen::Detail { movie_index } => {
                let title = &self.movie.items[*movie_index].title;
                vec![
                    ("Movies".to_string(), Some(BreadcrumbNav::MovieLibrary)),
                    (title.clone(), None),
                ]
            }
            Screen::ShowLibrary => vec![("TV Shows".to_string(), None)],
            Screen::ShowDetail { show_index } => {
                let title = &self.show.items[*show_index].title;
                vec![
                    ("TV Shows".to_string(), Some(BreadcrumbNav::ShowLibrary)),
                    (title.clone(), None),
                ]
            }
            Screen::SeasonDetail {
                show_index,
                season_index,
            } => {
                let show_title = &self.show.items[*show_index].title;
                let season = &self.show.seasons[*season_index];
                let season_label = season
                    .title
                    .clone()
                    .unwrap_or_else(|| format!("Season {}", season.season_number));
                vec![
                    ("TV Shows".to_string(), Some(BreadcrumbNav::ShowLibrary)),
                    (
                        show_title.clone(),
                        Some(BreadcrumbNav::ShowDetail {
                            show_index: *show_index,
                        }),
                    ),
                    (season_label, None),
                ]
            }
            Screen::Logs { .. } => vec![("Logs".to_string(), None)],
            Screen::Player(_) => vec![],
        }
    }

    fn return_to_movie_library(&mut self) -> Task<Message> {
        self.screen = Screen::Library;
        iced::widget::operation::scroll_to(
            movie::library::LIBRARY_SCROLLABLE_ID,
            iced::widget::scrollable::AbsoluteOffset {
                x: 0.0,
                y: self.movie.scroll_offset,
            },
        )
    }

    fn return_to_show_library(&mut self) -> Task<Message> {
        self.screen = Screen::ShowLibrary;
        iced::widget::operation::scroll_to(
            show::library::SHOW_LIBRARY_SCROLLABLE_ID,
            iced::widget::scrollable::AbsoluteOffset {
                x: 0.0,
                y: self.show.scroll_offset,
            },
        )
    }

    fn capture_screen_anchor(&self) -> ScreenAnchor {
        match &self.screen {
            Screen::Detail { movie_index } => ScreenAnchor::MovieDetail {
                movie_id: self.movie.items[*movie_index].id,
            },
            Screen::ShowDetail { show_index } => ScreenAnchor::ShowDetail {
                show_id: self.show.items[*show_index].id,
            },
            Screen::SeasonDetail {
                show_index,
                season_index,
            } => ScreenAnchor::SeasonDetail {
                show_id: self.show.items[*show_index].id,
                season_id: self.show.seasons[*season_index].id,
            },
            _ => ScreenAnchor::None,
        }
    }

    fn reanchor_screen(&mut self, anchor: ScreenAnchor) -> Task<Message> {
        match anchor {
            ScreenAnchor::None => Task::none(),
            ScreenAnchor::MovieDetail { movie_id } => {
                match self.movie.items.iter().position(|m| m.id == movie_id) {
                    Some(idx) => {
                        let item = &self.movie.items[idx];
                        self.movie.tmdb_state = Some(movie::tmdb_matcher::TmdbMatcherState::new(
                            idx,
                            &item.title,
                            item.tmdb_id.is_some(),
                        ));
                        self.screen = Screen::Detail { movie_index: idx };
                        Task::none()
                    }
                    None => self.return_to_movie_library(),
                }
            }
            ScreenAnchor::ShowDetail { show_id } => {
                match self.show.items.iter().position(|s| s.id == show_id) {
                    Some(idx) => {
                        self.load_seasons_for_show(idx);
                        let item = &self.show.items[idx];
                        self.show.tmdb_state = Some(show::tmdb_matcher::ShowTmdbMatcherState::new(
                            idx,
                            &item.title,
                            item.tmdb_id.is_some(),
                        ));
                        self.screen = Screen::ShowDetail { show_index: idx };
                        Task::none()
                    }
                    None => self.return_to_show_library(),
                }
            }
            ScreenAnchor::SeasonDetail { show_id, season_id } => {
                let Some(show_idx) = self.show.items.iter().position(|s| s.id == show_id) else {
                    return self.return_to_show_library();
                };
                self.load_seasons_for_show(show_idx);
                let Some(season_idx) = self.show.seasons.iter().position(|s| s.id == season_id)
                else {
                    let item = &self.show.items[show_idx];
                    self.show.tmdb_state = Some(show::tmdb_matcher::ShowTmdbMatcherState::new(
                        show_idx,
                        &item.title,
                        item.tmdb_id.is_some(),
                    ));
                    self.screen = Screen::ShowDetail {
                        show_index: show_idx,
                    };
                    return Task::none();
                };
                self.load_episodes_for_season(season_idx);
                self.screen = Screen::SeasonDetail {
                    show_index: show_idx,
                    season_index: season_idx,
                };
                Task::none()
            }
        }
    }

    /// Save playback position or mark as watched, depending on how much was watched.
    fn check_and_mark_watched(&mut self, current_time: f64, total_duration: f64) {
        let watched = total_duration > 0.0 && current_time / total_duration >= 0.80;
        let save_pos = !watched && current_time > 5.0;

        if let Some(movie_index) = self.playing_movie_index {
            let item = &mut self.movie.items[movie_index];
            if watched {
                if !item.watched {
                    if let Err(e) = self.db.set_movie_watched(true, item.id) {
                        log::error!("failed to auto-mark movie watched: {e}");
                    } else {
                        log::info!("auto-marked movie '{}' as watched", item.title);
                        item.watched = true;
                    }
                }
                if item.playback_position.is_some() {
                    if let Err(e) = self.db.clear_movie_playback(item.id) {
                        log::error!("failed to clear movie playback position: {e}");
                    } else {
                        item.playback_position = None;
                    }
                }
            } else if save_pos {
                if let Err(e) = self.db.update_movie_playback(current_time, item.id) {
                    log::error!("failed to save movie playback position: {e}");
                } else {
                    item.playback_position = Some(current_time);
                }
            }
        } else if let Some(file) = self.last_played_episode_file.clone() {
            if let Some(ep) = self
                .show
                .season_episodes
                .iter_mut()
                .find(|e| e.file == file)
            {
                if watched {
                    if !ep.watched {
                        if let Err(e) = self.db.set_episode_watched(true, ep.id) {
                            log::error!("failed to auto-mark episode watched: {e}");
                        } else {
                            log::info!("auto-marked episode as watched");
                            ep.watched = true;
                        }
                    }
                    if ep.playback_position.is_some() {
                        if let Err(e) = self.db.clear_episode_playback(ep.id) {
                            log::error!("failed to clear episode playback position: {e}");
                        } else {
                            ep.playback_position = None;
                        }
                    }
                    if let Ok(Some(next)) = self.db.find_next_queueable_episode(ep.id) {
                        if let Err(e) = self.db.update_episode_playback(0.0, next.id) {
                            log::error!("failed to queue next episode into continue watching: {e}");
                        }
                    }
                } else if save_pos {
                    if let Err(e) = self.db.update_episode_playback(current_time, ep.id) {
                        log::error!("failed to save episode playback position: {e}");
                    } else {
                        ep.playback_position = Some(current_time);
                    }
                }
            }
        }
    }

    fn restore_return_screen(&mut self) -> Task<Message> {
        self.load_home_data();
        match self.return_screen.clone() {
            ReturnScreen::Home => {
                self.screen = Screen::Home;
                Task::none()
            }
            ReturnScreen::MovieLibrary => {
                self.screen = Screen::Library;
                Task::none()
            }
            ReturnScreen::MovieDetail { movie_index } => {
                self.movie.tmdb_state = Some(movie::tmdb_matcher::TmdbMatcherState::new(
                    movie_index,
                    &self.movie.items[movie_index].title,
                    self.movie.items[movie_index].tmdb_id.is_some(),
                ));
                self.screen = Screen::Detail { movie_index };
                Task::none()
            }
            ReturnScreen::ShowLibrary => self.return_to_show_library(),
            ReturnScreen::SeasonDetail {
                show_index,
                season_index,
            } => {
                self.load_seasons_for_show(show_index);
                self.load_episodes_for_season(season_index);
                self.screen = Screen::SeasonDetail {
                    show_index,
                    season_index,
                };
                Task::none()
            }
        }
    }

    fn next_episode_file(&self) -> Option<String> {
        let last = self.last_played_episode_file.as_ref()?;
        let idx = self
            .show
            .season_episodes
            .iter()
            .position(|e| &e.file == last)?;
        self.show
            .season_episodes
            .get(idx + 1)
            .map(|e| e.file.clone())
    }

    fn build_continue_watching(
        movies: Vec<crate::database::entities::ContinueWatchingMovie>,
        episodes: Vec<crate::database::entities::ContinueWatchingEpisode>,
        cache_dir: &Path,
    ) -> Vec<home::ContinueWatchingItem> {
        let mut items: Vec<home::ContinueWatchingItem> = Vec::new();

        for m in movies {
            let abs_file = PathBuf::from(&m.library_root_path)
                .join(&m.file)
                .to_string_lossy()
                .to_string();
            items.push(home::ContinueWatchingItem {
                title: m.original_title.unwrap_or(m.title),
                file: abs_file,
                backdrop_path: cache_image_path(cache_dir, m.backdrop_filename),
                playback_position: m.playback_position,
                duration: m.duration,
                last_playback: m.last_playback,
                media_type: home::ContinueWatchingMediaType::Movie { id: m.id },
            });
        }

        for e in episodes {
            let abs_file = PathBuf::from(&e.library_root_path)
                .join(&e.file)
                .to_string_lossy()
                .to_string();
            let ep_title = e.title.as_deref().unwrap_or("Unknown");
            let title = format!(
                "{} — S{:02}E{:02} {}",
                e.show_title, e.season_number, e.episode_number, ep_title
            );
            items.push(home::ContinueWatchingItem {
                title,
                file: abs_file,
                backdrop_path: cache_image_path(cache_dir, e.backdrop_filename),
                playback_position: e.playback_position,
                duration: e.duration,
                last_playback: e.last_playback,
                media_type: home::ContinueWatchingMediaType::Episode {
                    show_id: e.show_id,
                    season_id: e.season_id,
                },
            });
        }

        items.sort_unstable_by(|a, b| b.last_playback.cmp(&a.last_playback));

        items
    }

    fn load_home_data(&mut self) {
        self.home.cw_offset = 0;
        self.home.movies_offset = 0;
        self.home.seasons_offset = 0;

        let cache_dir = images_cache_dir();

        self.home.continue_watching = Self::build_continue_watching(
            self.db.find_continue_watching_movies().unwrap_or_default(),
            self.db
                .find_continue_watching_episodes()
                .unwrap_or_default(),
            &cache_dir,
        );

        self.home.recent_movies = self
            .db
            .find_recent_movies_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_movie_library_item(row, &cache_dir))
            .collect();

        self.home.recent_seasons = self
            .db
            .find_recent_seasons_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_recent_season_item(row, &cache_dir))
            .collect();
    }

    fn refresh_libraries(&mut self) {
        let cache_dir = images_cache_dir();

        self.movie.items = self
            .db
            .find_movies_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_movie_library_item(row, &cache_dir))
            .collect();

        self.show.items = self
            .db
            .find_shows_with_covers()
            .unwrap_or_default()
            .into_iter()
            .map(|row| build_show_library_item(row, &cache_dir))
            .collect();
    }

    fn init_player(mut ctx: Player, volume: f32, muted: bool) -> Screen {
        let total_duration = ctx.media_info().total_duration;
        let current_audio_index = ctx.media_info().current_audio_index;
        let _screensaver_inhibitor = ScreensaverInhibitor::new();

        let audio_streams: Vec<AudioStreamOption> = ctx
            .media_info()
            .audio_streams
            .iter()
            .enumerate()
            .map(|(i, s)| {
                let lang = s.language.as_deref().unwrap_or("und");
                let ch = match s.channels {
                    1 => "mono".to_string(),
                    2 => "stereo".to_string(),
                    n => format!("{n}ch"),
                };
                AudioStreamOption {
                    index: s.index,
                    label: format!("#{} {} ({})", i + 1, lang, ch),
                }
            })
            .collect();

        let selected_audio_stream = audio_streams
            .iter()
            .find(|s| s.index == current_audio_index)
            .cloned();

        ctx.set_volume(volume);
        ctx.set_muted(muted);
        ctx.start().expect("start player");

        Screen::Player(player::State {
            ctx,
            frames_processed: 0,
            frames_dropped: 0,
            current_frame: None,
            last_click: None,
            is_fullscreen: false,
            current_time: 0.0,
            total_duration,
            paused: false,
            audio_streams,
            selected_audio_stream,
            volume,
            muted,
            yuv_buffer: None,
            _screensaver_inhibitor,
        })
    }

    fn load_seasons_for_show(&mut self, show_index: usize) {
        let show_id = self.show.items[show_index].id;
        let cache_dir = images_cache_dir();

        self.show.seasons = self
            .db
            .find_seasons_with_covers(show_id)
            .unwrap_or_default()
            .into_iter()
            .map(|s| SeasonItem {
                id: s.id,
                season_number: s.season_number,
                title: s.title,
                cover_path: cache_image_path(&cache_dir, s.cover_filename),
                description: s.description,
                rating: s.rating,
                watched: s.watched,
            })
            .collect();
    }

    fn load_episodes_for_season(&mut self, season_index: usize) {
        let season_id = self.show.seasons[season_index].id;
        self.show.season_episodes = self
            .db
            .find_episodes_with_library_by_season(season_id)
            .unwrap_or_default()
            .into_iter()
            .map(|e| {
                let abs_file = PathBuf::from(&e.library_root_path)
                    .join(&e.file)
                    .to_string_lossy()
                    .to_string();
                EpisodeItem {
                    id: e.id,
                    episode_number: e.episode_number,
                    title: e.title,
                    file: abs_file,
                    watched: e.watched,
                    playback_position: e.playback_position,
                }
            })
            .collect();
    }

    fn update(&mut self, msg: Message) -> Task<Message> {
        match msg {
            Message::Player(p) => {
                let Screen::Player(state) = &mut self.screen else {
                    return Task::none();
                };
                let task = player::update(state, p.clone());
                match p {
                    player::Message::CommitVolume => {
                        self.audio_volume = state.volume;
                        self.audio_muted = state.muted;
                        if let Err(e) = self
                            .db
                            .set_host("audio_volume", &format!("{:.3}", state.volume))
                        {
                            log::error!("persist audio_volume: {e}");
                        }
                        if let Err(e) = self
                            .db
                            .set_host("audio_muted", if state.muted { "1" } else { "0" })
                        {
                            log::error!("persist audio_muted: {e}");
                        }
                    }
                    player::Message::ToggleMute | player::Message::MKey(_) => {
                        self.audio_muted = state.muted;
                        if let Err(e) = self
                            .db
                            .set_host("audio_muted", if state.muted { "1" } else { "0" })
                        {
                            log::error!("persist audio_muted: {e}");
                        }
                    }
                    _ => {}
                }
                return task;
            }
            // Navigation
            Message::SwitchTab(tab) => {
                self.search_query.clear();
                return match tab {
                    Tab::Home => {
                        self.screen = Screen::Home;
                        Task::none()
                    }
                    Tab::Movies => self.return_to_movie_library(),
                    Tab::TvShows => self.return_to_show_library(),
                };
            }
            Message::HomeMovieClicked(home_idx) => {
                let movie_id = self.home.recent_movies[home_idx].id;
                if let Some(index) = self.movie.items.iter().position(|m| m.id == movie_id) {
                    self.movie.tmdb_state = Some(movie::tmdb_matcher::TmdbMatcherState::new(
                        index,
                        &self.movie.items[index].title,
                        self.movie.items[index].tmdb_id.is_some(),
                    ));
                    self.screen = Screen::Detail { movie_index: index };
                }
                return Task::none();
            }
            Message::HomeSeasonClicked(home_idx) => {
                let show_id = self.home.recent_seasons[home_idx].show_id;
                if let Some(index) = self.show.items.iter().position(|s| s.id == show_id) {
                    self.load_seasons_for_show(index);
                    let item = &self.show.items[index];
                    self.show.tmdb_state = Some(show::tmdb_matcher::ShowTmdbMatcherState::new(
                        index,
                        &item.title,
                        item.tmdb_id.is_some(),
                    ));
                    self.screen = Screen::ShowDetail { show_index: index };
                }
                return Task::none();
            }
            Message::ContinueWatchingClicked(index) => {
                let item = &self.home.continue_watching[index];
                let file = item.file.clone();
                let pos = item.playback_position;
                self.return_screen = ReturnScreen::Home;
                match &item.media_type {
                    home::ContinueWatchingMediaType::Movie { id } => {
                        let id = *id;
                        let movie_index = self
                            .movie
                            .items
                            .iter()
                            .position(|m| m.id == id)
                            .unwrap_or(0);
                        self.playing_movie_index = Some(movie_index);
                    }
                    home::ContinueWatchingMediaType::Episode { show_id, season_id } => {
                        let show_id = *show_id;
                        let season_id = *season_id;
                        let show_index = self
                            .show
                            .items
                            .iter()
                            .position(|s| s.id == show_id)
                            .unwrap_or(0);
                        self.load_seasons_for_show(show_index);
                        let season_index = self
                            .show
                            .seasons
                            .iter()
                            .position(|s| s.id == season_id)
                            .unwrap_or(0);
                        self.load_episodes_for_season(season_index);
                        self.last_played_episode_file = Some(file.clone());
                        self.playing_movie_index = None;
                    }
                }
                match Player::new(&file) {
                    Ok(player) => {
                        let duration = player.media_info().total_duration;
                        if let Err(e) = self.db.upsert_media_meta(&file, duration) {
                            log::error!("failed to save media meta: {e}");
                        }
                        self.screen = Self::init_player(player, self.audio_volume, self.audio_muted);
                        if let Screen::Player(state) = &self.screen {
                            if let Err(e) = state.ctx.seek(pos) {
                                log::warn!("failed to seek to continue-watching position: {e}");
                            }
                        }
                    }
                    Err(e) => log::error!("failed to play from continue watching: {e}"),
                }
                return Task::none();
            }
            Message::Home(h) => return home::update(&mut self.home, h),
            Message::NavigateBreadcrumb(ref target) => match target {
                BreadcrumbNav::MovieLibrary => return self.return_to_movie_library(),
                BreadcrumbNav::ShowLibrary => return self.return_to_show_library(),
                BreadcrumbNav::ShowDetail { show_index } => {
                    let show_index = *show_index;
                    self.load_seasons_for_show(show_index);
                    let item = &self.show.items[show_index];
                    self.show.tmdb_state = Some(show::tmdb_matcher::ShowTmdbMatcherState::new(
                        show_index,
                        &item.title,
                        item.tmdb_id.is_some(),
                    ));
                    self.screen = Screen::ShowDetail { show_index };
                    return Task::none();
                }
            },
            // Movie screens
            Message::MovieDetail(index) => {
                self.search_query.clear();
                self.movie.tmdb_state = Some(movie::tmdb_matcher::TmdbMatcherState::new(
                    index,
                    &self.movie.items[index].title,
                    self.movie.items[index].tmdb_id.is_some(),
                ));
                self.screen = Screen::Detail { movie_index: index };
                return Task::none();
            }
            Message::PlayMovie(index) => {
                self.search_query.clear();
                let file = self.movie.items[index].file.clone();
                let saved_pos = self.movie.items[index].playback_position;
                self.playing_movie_index = Some(index);
                self.return_screen = match &self.screen {
                    Screen::Detail { .. } => ReturnScreen::MovieDetail { movie_index: index },
                    _ => ReturnScreen::MovieLibrary,
                };
                match Player::new(&file) {
                    Ok(player) => {
                        let duration = player.media_info().total_duration;
                        if let Err(e) = self.db.upsert_media_meta(&file, duration) {
                            log::error!("failed to save media meta: {e}");
                        }
                        self.screen = Self::init_player(player, self.audio_volume, self.audio_muted);
                        if let Some(pos) = saved_pos {
                            if let Screen::Player(state) = &self.screen {
                                if let Err(e) = state.ctx.seek(pos) {
                                    log::warn!("failed to seek to saved position: {e}");
                                }
                            }
                        }
                    }
                    Err(e) => log::error!("failed to open movie: {e}"),
                }
                return Task::none();
            }
            Message::Movie(m) => {
                let needs_home_reload = matches!(
                    &m,
                    movie::Message::ToggleWatched(_) | movie::Message::TmdbMatchCompleted(Ok(_))
                );
                let task = movie::update(&mut self.movie, m, &mut self.db, &self.tmdb_api_token);
                if needs_home_reload {
                    self.load_home_data();
                }
                return task;
            }
            // TV show screens
            Message::ShowDetail(index) => {
                self.search_query.clear();
                self.load_seasons_for_show(index);
                let item = &self.show.items[index];
                self.show.tmdb_state = Some(show::tmdb_matcher::ShowTmdbMatcherState::new(
                    index,
                    &item.title,
                    item.tmdb_id.is_some(),
                ));
                self.screen = Screen::ShowDetail { show_index: index };
                return Task::none();
            }
            Message::ShowSeasonDetail(season_idx) => {
                self.search_query.clear();
                let show_index = match self.screen {
                    Screen::ShowDetail { show_index } => show_index,
                    _ => return Task::none(),
                };
                self.load_episodes_for_season(season_idx);
                self.screen = Screen::SeasonDetail {
                    show_index,
                    season_index: season_idx,
                };
                return Task::none();
            }
            Message::PlayEpisode(ref file) => {
                self.search_query.clear();
                self.last_played_episode_file = Some(file.clone());
                self.playing_movie_index = None;
                self.return_screen = match self.screen {
                    Screen::SeasonDetail {
                        show_index,
                        season_index,
                    } => ReturnScreen::SeasonDetail {
                        show_index,
                        season_index,
                    },
                    _ => ReturnScreen::ShowLibrary,
                };
                let saved_pos = self
                    .show
                    .season_episodes
                    .iter()
                    .find(|e| &e.file == file)
                    .and_then(|e| e.playback_position);
                match Player::new(file) {
                    Ok(player) => {
                        let duration = player.media_info().total_duration;
                        if let Err(e) = self.db.upsert_media_meta(file, duration) {
                            log::error!("failed to save media meta: {e}");
                        }
                        self.screen = Self::init_player(player, self.audio_volume, self.audio_muted);
                        if let Some(pos) = saved_pos {
                            if let Screen::Player(state) = &self.screen {
                                if let Err(e) = state.ctx.seek(pos) {
                                    log::warn!("failed to seek to saved position: {e}");
                                }
                            }
                        }
                    }
                    Err(e) => log::error!("failed to open episode: {e}"),
                }
                return Task::none();
            }
            Message::Show(m) => {
                let needs_seasons_reload = matches!(&m, show::Message::TmdbMatchCompleted(Ok(_)));
                let task = show::update(&mut self.show, m, &mut self.db, &self.tmdb_api_token);
                if needs_seasons_reload {
                    if let Some(ts) = &self.show.tmdb_state {
                        let show_index = ts.show_index;
                        self.load_seasons_for_show(show_index);
                    }
                    self.load_home_data();
                }
                return task;
            }
            Message::ToggleShowWatched => {
                if let Screen::ShowDetail { show_index } = self.screen {
                    let item = &mut self.show.items[show_index];
                    let new_watched = !item.watched;
                    let id = item.id;
                    if let Err(e) = self.db.transaction(|tx| {
                        queries::set_show_watched.exec(tx, params![new_watched, id])?;
                        queries::set_seasons_watched_by_show.exec(tx, params![new_watched, id])?;
                        queries::set_episodes_watched_by_show.exec(tx, params![new_watched, id])?;
                        if new_watched {
                            queries::clear_episodes_playback_by_show.exec(tx, params![id])?;
                        }
                        Ok(())
                    }) {
                        log::error!("failed to set show watched: {e}");
                    } else {
                        item.watched = new_watched;
                        for season in self.show.seasons.iter_mut() {
                            season.watched = new_watched;
                        }
                        if new_watched {
                            for ep in self.show.season_episodes.iter_mut() {
                                ep.playback_position = None;
                            }
                        }
                    }
                }
                self.load_home_data();
                return Task::none();
            }
            Message::ToggleSeasonWatched => {
                if let Screen::SeasonDetail { season_index, .. } = self.screen {
                    let item = &mut self.show.seasons[season_index];
                    let new_watched = !item.watched;
                    let id = item.id;
                    if let Err(e) = self.db.transaction(|tx| {
                        queries::set_season_watched.exec(tx, params![new_watched, id])?;
                        queries::set_episodes_watched_by_season
                            .exec(tx, params![new_watched, id])?;
                        if new_watched {
                            queries::clear_episodes_playback_by_season.exec(tx, params![id])?;
                        }
                        Ok(())
                    }) {
                        log::error!("failed to set season watched: {e}");
                    } else {
                        item.watched = new_watched;
                        for ep in self.show.season_episodes.iter_mut() {
                            ep.watched = new_watched;
                            if new_watched {
                                ep.playback_position = None;
                            }
                        }
                    }
                }
                self.load_home_data();
                return Task::none();
            }
            Message::ToggleEpisodeWatched(ep_index) => {
                let item = &mut self.show.season_episodes[ep_index];
                let new_watched = !item.watched;
                if let Err(e) = self.db.set_episode_watched(new_watched, item.id) {
                    log::error!("failed to set episode watched: {e}");
                } else {
                    item.watched = new_watched;
                    if new_watched && item.playback_position.is_some() {
                        if let Err(e) = self.db.clear_episode_playback(item.id) {
                            log::error!("failed to clear episode playback position: {e}");
                        } else {
                            item.playback_position = None;
                        }
                    }
                }
                self.load_home_data();
                return Task::none();
            }
            Message::SearchQueryChanged(query) => {
                self.search_query = query;
                self.movie.scroll_offset = 0.0;
                self.show.scroll_offset = 0.0;
                return Task::none();
            }
            Message::FocusSearch => {
                return iced::widget::operation::focus(shared::nav::SEARCH_INPUT_ID);
            }
            Message::ZoomIn => {
                self.ui_scale = (self.ui_scale + 0.1).min(3.0);
                self.movie.scroll_offset = 0.0;
                self.show.scroll_offset = 0.0;
                let _ = self
                    .db
                    .set_host("ui_scale", &format!("{:.1}", self.ui_scale));
                return Task::none();
            }
            Message::ZoomOut => {
                self.ui_scale = (self.ui_scale - 0.1).max(1.0);
                self.movie.scroll_offset = 0.0;
                self.show.scroll_offset = 0.0;
                let _ = self
                    .db
                    .set_host("ui_scale", &format!("{:.1}", self.ui_scale));
                return Task::none();
            }
            Message::OpenLogs => {
                let content = std::fs::read_to_string(&self.log_path)
                    .unwrap_or_else(|e| format!("Failed to read log file: {}", e));
                self.screen = Screen::Logs { content };
                return Task::none();
            }
            Message::CloseLogs => {
                self.screen = Screen::Home;
                return Task::none();
            }
            Message::QuitApp(window_id) => {
                if let Screen::Player(state) = &self.screen {
                    self.check_and_mark_watched(state.current_time, state.total_duration);
                }
                return iced::window::close(window_id);
            }
            Message::EscapeKey(_window_id) => {
                match self.screen {
                    Screen::Home | Screen::Library | Screen::ShowLibrary => {
                        return Task::none();
                    }
                    Screen::Detail { .. } => {
                        return self.return_to_movie_library();
                    }
                    Screen::ShowDetail { .. } => {
                        return self.return_to_show_library();
                    }
                    Screen::SeasonDetail { show_index, .. } => {
                        self.load_seasons_for_show(show_index);
                        let item = &self.show.items[show_index];
                        self.show.tmdb_state = Some(show::tmdb_matcher::ShowTmdbMatcherState::new(
                            show_index,
                            &item.title,
                            item.tmdb_id.is_some(),
                        ));
                        self.screen = Screen::ShowDetail { show_index };
                        return Task::none();
                    }
                    Screen::Logs { .. } => {
                        self.screen = Screen::Home;
                        return Task::none();
                    }
                    Screen::Player(_) => {} // fall through to Player handling below
                }
            }
            Message::MouseMoved => {
                self.last_mouse_move = Instant::now();
                return Task::none();
            }
            Message::StartScan => {
                if self.scan_handle.is_some() {
                    return Task::none();
                }

                self.scan_handle = Some(scanner_task::spawn(self.db.get_db_path()));
                self.spinner_frame = 0;
                return Task::none();
            }
            Message::CancelScan => {
                if let Some(h) = &self.scan_handle {
                    h.cancel.store(true, std::sync::atomic::Ordering::Relaxed);
                }
                return Task::none();
            }
            Message::SpinnerTick => {
                self.spinner_frame = self.spinner_frame.wrapping_add(1);
                return Task::none();
            }
            Message::Scan(ScanEvent::Finished) => {
                self.scan_handle = None;
                log::info!("scan finished");

                let anchor = self.capture_screen_anchor();
                self.refresh_libraries();
                self.load_home_data();
                return self.reanchor_screen(anchor);
            }
            Message::Scan(ScanEvent::Error(ref e)) => {
                log::error!("scan failed: {e}");
                return Task::none();
            }
            _ => {}
        }

        let Screen::Player(player::State {
            ctx,
            current_frame,
            frames_dropped,
            frames_processed,
            is_fullscreen,
            current_time,
            total_duration,
            paused,
            yuv_buffer,
            ..
        }) = &mut self.screen
        else {
            return Task::none();
        };

        match msg {
            Message::EscapeKey(window_id) => {
                if *is_fullscreen {
                    *is_fullscreen = false;
                    return iced::window::set_mode(window_id, iced::window::Mode::Windowed);
                }
                // Stop playback and return to previous screen
                let ct = *current_time;
                let td = *total_duration;
                self.check_and_mark_watched(ct, td);
                self.restore_return_screen()
            }
            Message::Tick(_) => {
                if ctx.video_ctx.is_finished() && !ctx.video_ctx.has_frames().unwrap_or(false) {
                    ctx.video_ctx.set_state(PlayerState::Stopped);
                    let ct = *current_time;
                    let td = *total_duration;
                    let was_fullscreen = *is_fullscreen;
                    self.check_and_mark_watched(ct, td);
                    if self.play_next {
                        if self.last_played_episode_file.is_some() {
                            if let Some(next_file) = self.next_episode_file() {
                                log::info!("auto-playing next episode: {next_file}");
                                self.last_played_episode_file = Some(next_file.clone());
                                match Player::new(&next_file) {
                                    Ok(player) => {
                                        let duration = player.media_info().total_duration;
                                        if let Err(e) =
                                            self.db.upsert_media_meta(&next_file, duration)
                                        {
                                            log::error!("failed to save media meta: {e}");
                                        }
                                        self.screen = Self::init_player(player, self.audio_volume, self.audio_muted);
                                        if let Screen::Player(player::State {
                                            is_fullscreen, ..
                                        }) = &mut self.screen
                                        {
                                            *is_fullscreen = was_fullscreen;
                                        }
                                        return Task::none();
                                    }
                                    Err(e) => log::error!("failed to open next episode: {e}"),
                                }
                            }
                        }
                    }
                    let restore_task = self.restore_return_screen();
                    if was_fullscreen {
                        let exit_fs = iced::window::oldest().then(|id| {
                            if let Some(id) = id {
                                iced::window::set_mode(id, iced::window::Mode::Windowed)
                            } else {
                                Task::none()
                            }
                        });
                        return Task::batch([exit_fs, restore_task]);
                    }
                    return restore_task;
                }

                if *paused {
                    return Task::none();
                }

                let media = ctx.media_info();
                let frame_res = match player::video::process_frame(
                    ctx,
                    media.width,
                    media.height,
                    yuv_buffer,
                ) {
                    Ok(res) => res,
                    Err(e) => {
                        log::error!("failed to process the next frame: {e}");
                        return iced::exit();
                    }
                };

                let player::video::FrameResult {
                    frame_data,
                    clock,
                    action,
                    buf_len,
                } = frame_res;

                *frames_processed += 1;
                if matches!(action, FrameAction::DropFrame) {
                    *frames_dropped += 1;
                }

                if frame_data.is_some() {
                    *current_frame = frame_data;
                }

                if clock > 0.0 {
                    *current_time = clock;
                }

                log::trace!(
                    "[video]: frame={} clock={:.3} video_buffer={} dropped={} action={}",
                    frames_processed,
                    clock,
                    buf_len,
                    frames_dropped,
                    action
                );

                Task::none()
            }
            _ => Task::none(),
        }
    }

    fn subscription(&self) -> iced::Subscription<Message> {
        let escape_key = iced::event::listen_raw(|event, _status, window| match event {
            iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
                key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Escape),
                ..
            }) => Some(Message::EscapeKey(window)),
            _ => None,
        });

        let ctrl_q = iced::event::listen_raw(|event, _status, window| match event {
            iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
                key: iced::keyboard::Key::Character(ref c),
                modifiers,
                ..
            }) if c.as_str() == "q" && modifiers.control() => Some(Message::QuitApp(window)),
            _ => None,
        });

        let close_requested = iced::event::listen_raw(|event, _status, window| match event {
            iced::Event::Window(iced::window::Event::CloseRequested) => {
                Some(Message::QuitApp(window))
            }
            _ => None,
        });

        let scan_sub = if let Some(handle) = &self.scan_handle {
            let rx = ScanReceiver::new(handle.events.clone());
            fn build(rx: &ScanReceiver) -> impl iced::futures::Stream<Item = Message> + use<> {
                let rx = rx.rx.clone();
                iced::stream::channel(
                    32,
                    move |mut out: iced::futures::channel::mpsc::Sender<Message>| async move {
                        while let Ok(ev) = rx.recv().await {
                            let _ = out.send(Message::Scan(ev)).await;
                        }
                    },
                )
            }

            iced::Subscription::run_with(rx, build)
        } else {
            iced::Subscription::none()
        };

        let spinner_sub = if self.scan_handle.is_some() {
            iced::time::every(std::time::Duration::from_millis(80)).map(|_| Message::SpinnerTick)
        } else {
            iced::Subscription::none()
        };

        if !matches!(self.screen, Screen::Player(_)) {
            let ctrl_f = iced::event::listen_raw(|event, _status, _window| match event {
                iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: iced::keyboard::Key::Character(ref c),
                    modifiers,
                    ..
                }) if c.as_str() == "f" && modifiers.control() => Some(Message::FocusSearch),
                _ => None,
            });
            let zoom = iced::event::listen_raw(|event, _status, _window| match event {
                iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: iced::keyboard::Key::Character(ref c),
                    modifiers,
                    ..
                }) if modifiers.control() => match c.as_str() {
                    "=" | "+" => Some(Message::ZoomIn),
                    "-" | "_" => Some(Message::ZoomOut),
                    _ => None,
                },
                _ => None,
            });
            return iced::Subscription::batch([
                escape_key,
                ctrl_f,
                ctrl_q,
                close_requested,
                zoom,
                scan_sub,
                spinner_sub,
            ]);
        }

        let tick = iced::window::frames().map(|_| Message::Tick(std::time::Instant::now()));

        let player_events = iced::event::listen_raw(|event, _status, window| match event {
            iced::Event::Mouse(iced::mouse::Event::ButtonPressed(iced::mouse::Button::Left)) => {
                Some(Message::Player(player::Message::LeftClick(window)))
            }
            iced::Event::Mouse(iced::mouse::Event::CursorMoved { .. }) => Some(Message::MouseMoved),
            iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
                key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Space),
                ..
            }) => Some(Message::Player(player::Message::SpaceKey(window))),
            iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
                key: iced::keyboard::Key::Character(ref c),
                ..
            }) if c.eq_ignore_ascii_case("m") => {
                Some(Message::Player(player::Message::MKey(window)))
            }
            _ => None,
        });

        iced::Subscription::batch([
            tick,
            escape_key,
            ctrl_q,
            close_requested,
            player_events,
            scan_sub,
            spinner_sub,
        ])
    }

    fn view(&self) -> Element<'_, Message> {
        // Player: no chrome, return directly
        if let Screen::Player(player::State {
            ctx,
            current_frame,
            current_time,
            total_duration,
            is_fullscreen,
            audio_streams,
            selected_audio_stream,
            volume,
            muted,
            ..
        }) = &self.screen
        {
            return player::view(
                ctx,
                current_frame,
                *current_time,
                *total_duration,
                *is_fullscreen,
                audio_streams,
                selected_audio_stream,
                *volume,
                *muted,
                self.last_mouse_move,
            );
        }

        let tab_bar = shared::nav::tab_bar(
            self.active_tab(),
            &self.search_query,
            self.scan_handle.is_some(),
            self.spinner_frame,
        );
        let segments = self.breadcrumb_segments();

        let s = self.ui_scale;
        let content: Element<_> = match &self.screen {
            Screen::Home => home::view(
                &self.home.continue_watching,
                &self.home.recent_movies,
                &self.home.recent_seasons,
                self.home.cw_offset,
                self.home.movies_offset,
                self.home.seasons_offset,
                s,
            ),
            Screen::Library => movie::library::view(
                &self.movie.items,
                &self.search_query,
                self.movie.scroll_offset,
                self.movie.viewport_height,
                self.movie.hovered_item,
                s,
            ),
            Screen::Detail { movie_index } => {
                let tmdb_widget = if self.tmdb_api_token.is_some() {
                    self.movie
                        .tmdb_state
                        .as_ref()
                        .map(|state| movie::tmdb_matcher::view(state))
                } else {
                    None
                };
                movie::detail::view(
                    &self.movie.items[*movie_index],
                    *movie_index,
                    tmdb_widget,
                    s,
                )
            }
            Screen::ShowLibrary => show::library::view(
                &self.show.items,
                &self.search_query,
                self.show.scroll_offset,
                self.show.viewport_height,
                s,
            ),
            Screen::ShowDetail { show_index } => {
                let tmdb_widget = if self.tmdb_api_token.is_some() {
                    self.show
                        .tmdb_state
                        .as_ref()
                        .map(|state| show::tmdb_matcher::view(state))
                } else {
                    None
                };
                show::detail::view(
                    &self.show.items[*show_index],
                    &self.show.seasons,
                    tmdb_widget,
                    s,
                )
            }
            Screen::SeasonDetail {
                show_index,
                season_index,
            } => show::season_detail::view(
                &self.show.items[*show_index],
                &self.show.seasons[*season_index],
                &self.show.season_episodes,
                self.last_played_episode_file.as_deref(),
                s,
            ),
            Screen::Logs { content } => shared::logs::view(content),
            Screen::Player(_) => unreachable!(),
        };

        let mut page = iced::widget::column![tab_bar];

        if segments.len() > 1 {
            page = page.push(shared::nav::breadcrumb(segments, s));
        }

        page = page.push(content);

        iced::widget::container(page)
            .width(iced::Length::Fill)
            .height(iced::Length::Fill)
            .style(|_theme| iced::widget::container::Style {
                background: Some(iced::Background::Color(shared::styles::BG_APP)),
                ..Default::default()
            })
            .into()
    }
}
