use super::Message;
use super::library::LibraryItem;
use crate::database::Database;
use crate::tmdb::TmdbClient;
use crate::ui::shared::tmdb::TmdbResultDisplay;
use iced::{Element, Task};
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub(crate) struct TmdbSearchResultItem {
    pub(crate) tmdb_id: u32,
    pub(crate) title: String,
    pub(crate) original_title: String,
    pub(crate) release_date: String,
    pub(crate) overview: Option<String>,
    pub(crate) vote_average: Option<f64>,
    pub(crate) poster_path: Option<String>,
    pub(crate) backdrop_path: Option<String>,
    pub(crate) poster_image: Option<Vec<u8>>,
}

impl TmdbResultDisplay for TmdbSearchResultItem {
    fn title(&self) -> &str {
        &self.title
    }
    fn year_str(&self) -> &str {
        if self.release_date.len() >= 4 {
            &self.release_date[..4]
        } else {
            &self.release_date
        }
    }
    fn poster_image(&self) -> Option<&[u8]> {
        self.poster_image.as_deref()
    }
}

#[derive(Debug, Clone)]
pub(crate) struct MovieMatchData {
    pub(crate) tmdb_id: u32,
    pub(crate) original_title: String,
    pub(crate) description: Option<String>,
    pub(crate) rating: Option<f64>,
    pub(crate) poster: Option<(String, Vec<u8>)>,
    pub(crate) backdrop: Option<(String, Vec<u8>)>,
}

pub(crate) struct TmdbMatcherState {
    pub(crate) movie_index: usize,
    pub(crate) search_query: String,
    pub(crate) search_results: Vec<TmdbSearchResultItem>,
    pub(crate) searching: bool,
    pub(crate) search_expanded: bool,
    pub(crate) error: Option<String>,
}

impl TmdbMatcherState {
    pub(crate) fn new(movie_index: usize, title: &str, has_tmdb_id: bool) -> Self {
        Self {
            movie_index,
            search_query: title.to_string(),
            search_results: Vec::new(),
            searching: false,
            search_expanded: !has_tmdb_id,
            error: None,
        }
    }
}

pub(crate) fn update(
    state: &mut TmdbMatcherState,
    msg: Message,
    tmdb_api_token: &Option<String>,
) -> Task<crate::ui::Message> {
    match msg {
        Message::TmdbSearchQueryChanged(text) => {
            state.search_query = text;
            Task::none()
        }
        Message::TmdbToggleSearch => {
            state.search_expanded = !state.search_expanded;
            Task::none()
        }
        Message::TmdbSearchSubmit => {
            if state.search_query.trim().is_empty() {
                return Task::none();
            }
            state.searching = true;
            state.search_results.clear();
            state.error = None;

            let query = state.search_query.clone();
            let token = tmdb_api_token.clone().unwrap_or_default();

            Task::perform(
                async move {
                    let mut client = TmdbClient::new(token);
                    let results = client
                        .search_movie(&query, None)
                        .map_err(|e| e.to_string())?;

                    let img_config = client.image_configuration().map_err(|e| e.to_string())?;

                    let items: Vec<TmdbSearchResultItem> = results
                        .into_iter()
                        .map(|r| {
                            let poster_image = r
                                .get_poster_url(&img_config)
                                .and_then(|url| client.download_image(&url).ok())
                                .filter(|bytes| !bytes.is_empty());
                            TmdbSearchResultItem {
                                tmdb_id: r.id,
                                title: r.title,
                                original_title: r.original_title,
                                release_date: r.release_date.unwrap_or_default(),
                                overview: r.overview,
                                vote_average: r.vote_average,
                                poster_path: r.poster_path,
                                backdrop_path: r.backdrop_path,
                                poster_image,
                            }
                        })
                        .collect();

                    Ok(items)
                },
                |result| crate::ui::Message::Movie(Message::TmdbSearchCompleted(result)),
            )
        }
        Message::TmdbSearchCompleted(result) => {
            state.searching = false;
            match result {
                Ok(results) => state.search_results = results,
                Err(e) => state.error = Some(e),
            }
            Task::none()
        }
        Message::TmdbSelectMatch(idx) => {
            let result = state.search_results[idx].clone();
            state.searching = true;
            state.error = None;

            let token = tmdb_api_token.clone().unwrap_or_default();

            Task::perform(
                async move {
                    let mut client = TmdbClient::new(token);
                    let img_config = client.image_configuration().map_err(|e| e.to_string())?;

                    let poster = result.poster_path.as_ref().and_then(|path| {
                        let url = format!(
                            "{}/{}/{}",
                            img_config.secure_base_url,
                            img_config.poster_w342(),
                            path
                        );
                        let bytes = client.download_image(&url).ok()?;
                        if bytes.is_empty() {
                            return None;
                        }
                        Some((path.clone(), bytes))
                    });

                    let backdrop = result.backdrop_path.as_ref().and_then(|path| {
                        let url = format!("{}/w1280/{}", img_config.secure_base_url, path);
                        let bytes = client.download_image(&url).ok()?;
                        if bytes.is_empty() {
                            return None;
                        }
                        Some((path.clone(), bytes))
                    });

                    Ok(MovieMatchData {
                        tmdb_id: result.tmdb_id,
                        original_title: result.original_title,
                        description: result.overview,
                        rating: result.vote_average,
                        poster,
                        backdrop,
                    })
                },
                |result| crate::ui::Message::Movie(Message::TmdbMatchCompleted(result)),
            )
        }
        _ => Task::none(),
    }
}

/// Apply match data to database and update library item. Called from mod.rs on MovieTmdbMatchCompleted(Ok(data)).
pub(crate) fn apply_match(
    data: &MovieMatchData,
    movie_id: i64,
    db: &mut Database,
    movie_item: &mut LibraryItem,
) {
    if let Err(e) = db.update_movie_tmdb(
        movie_id,
        data.tmdb_id as i64,
        &data.original_title,
        data.description.as_deref(),
        data.rating,
    ) {
        log::error!("failed to update movie tmdb: {e}");
        return;
    }

    let cache_dir = dirs::cache_dir()
        .unwrap_or_else(|| PathBuf::from(".cache"))
        .join("myplayer")
        .join("images");

    if let Ok((cover_path, backdrop_path)) = crate::image_store::store_movie_images(
        db,
        movie_id,
        data.poster.clone(),
        data.backdrop.clone(),
        &cache_dir,
    ) {
        if let Some(path) = cover_path {
            movie_item.cover_path = Some(path);
        }
        if let Some(path) = backdrop_path {
            movie_item.backdrop_path = Some(path);
        }
    }

    movie_item.tmdb_id = Some(data.tmdb_id as i64);
}

pub(crate) fn view<'a>(state: &'a TmdbMatcherState) -> Element<'a, crate::ui::Message> {
    crate::ui::shared::tmdb::tmdb_search_section(
        state.search_expanded,
        state.searching,
        false,
        "",
        &state.search_query,
        "Search movie...",
        &state.search_results,
        &state.error,
        crate::ui::Message::Movie(Message::TmdbToggleSearch),
        |s| crate::ui::Message::Movie(Message::TmdbSearchQueryChanged(s)),
        crate::ui::Message::Movie(Message::TmdbSearchSubmit),
        |i| crate::ui::Message::Movie(Message::TmdbSelectMatch(i)),
    )
}
