use crate::app_style::NotedeckTextStyle;
use crate::imgcache::ImageCache;
use crate::ui::ProfilePic;
use crate::{colors, images, DisplayName};
use egui::load::TexturePoll;
use egui::{Frame, RichText, Sense, Widget};
use egui_extras::Size;
use nostrdb::ProfileRecord;

pub struct ProfilePreview<'a, 'cache> {
    profile: &'a ProfileRecord<'a>,
    cache: &'cache mut ImageCache,
    banner_height: Size,
}

impl<'a, 'cache> ProfilePreview<'a, 'cache> {
    pub fn new(profile: &'a ProfileRecord<'a>, cache: &'cache mut ImageCache) -> Self {
        let banner_height = Size::exact(80.0);
        ProfilePreview {
            profile,
            cache,
            banner_height,
        }
    }

    pub fn banner_height(&mut self, size: Size) {
        self.banner_height = size;
    }

    fn banner_texture(
        ui: &mut egui::Ui,
        profile: &ProfileRecord<'_>,
    ) -> Option<egui::load::SizedTexture> {
        // TODO: cache banner
        let banner = profile.record().profile().and_then(|p| p.banner());

        if let Some(banner) = banner {
            let texture_load_res =
                egui::Image::new(banner).load_for_size(ui.ctx(), ui.available_size());
            if let Ok(texture_poll) = texture_load_res {
                match texture_poll {
                    TexturePoll::Pending { .. } => {}
                    TexturePoll::Ready { texture, .. } => return Some(texture),
                }
            }
        }

        None
    }

    fn banner(ui: &mut egui::Ui, profile: &ProfileRecord<'_>) -> egui::Response {
        if let Some(texture) = Self::banner_texture(ui, profile) {
            images::aspect_fill(
                ui,
                Sense::hover(),
                texture.id,
                texture.size.x / texture.size.y,
            )
        } else {
            // TODO: default banner texture
            ui.label("")
        }
    }

    fn body(self, ui: &mut egui::Ui) {
        crate::ui::padding(12.0, ui, |ui| {
            ui.add(ProfilePic::new(self.cache, get_profile_url(self.profile)).size(80.0));
            ui.add(display_name_widget(get_display_name(self.profile), false));
            ui.add(about_section_widget(self.profile));
        });
    }
}

impl<'a, 'cache> egui::Widget for ProfilePreview<'a, 'cache> {
    fn ui(self, ui: &mut egui::Ui) -> egui::Response {
        ui.vertical(|ui| {
            ui.add_sized([ui.available_size().x, 80.0], |ui: &mut egui::Ui| {
                ProfilePreview::banner(ui, self.profile)
            });

            self.body(ui);
        })
        .response
    }
}

pub struct SimpleProfilePreview<'a, 'cache> {
    profile: &'a ProfileRecord<'a>,
    cache: &'cache mut ImageCache,
}

impl<'a, 'cache> SimpleProfilePreview<'a, 'cache> {
    pub fn new(profile: &'a ProfileRecord<'a>, cache: &'cache mut ImageCache) -> Self {
        SimpleProfilePreview { profile, cache }
    }
}

impl<'a, 'cache> egui::Widget for SimpleProfilePreview<'a, 'cache> {
    fn ui(self, ui: &mut egui::Ui) -> egui::Response {
        Frame::none()
            .show(ui, |ui| {
                ui.add(ProfilePic::new(self.cache, get_profile_url(self.profile)).size(48.0));
                ui.vertical(|ui| {
                    ui.add(display_name_widget(get_display_name(self.profile), true));
                });
            })
            .response
    }
}

mod previews {
    use super::*;
    use crate::test_data::test_profile_record;
    use crate::ui::{Preview, PreviewConfig, View};

    pub struct ProfilePreviewPreview<'a> {
        profile: ProfileRecord<'a>,
        cache: ImageCache,
    }

    impl<'a> ProfilePreviewPreview<'a> {
        pub fn new() -> Self {
            let profile = test_profile_record();
            let cache = ImageCache::new(ImageCache::rel_datadir().into());
            ProfilePreviewPreview { profile, cache }
        }
    }

    impl<'a> Default for ProfilePreviewPreview<'a> {
        fn default() -> Self {
            ProfilePreviewPreview::new()
        }
    }

    impl<'a> View for ProfilePreviewPreview<'a> {
        fn ui(&mut self, ui: &mut egui::Ui) {
            ProfilePreview::new(&self.profile, &mut self.cache).ui(ui);
        }
    }

    impl<'a, 'cache> Preview for ProfilePreview<'a, 'cache> {
        /// A preview of the profile preview :D
        type Prev = ProfilePreviewPreview<'a>;

        fn preview(_cfg: PreviewConfig) -> Self::Prev {
            ProfilePreviewPreview::new()
        }
    }
}

pub fn get_display_name<'a>(profile: &'a ProfileRecord<'a>) -> DisplayName<'a> {
    if let Some(name) = crate::profile::get_profile_name(profile) {
        name
    } else {
        DisplayName::One("??")
    }
}

pub fn get_profile_url<'a>(profile: &'a ProfileRecord<'a>) -> &'a str {
    if let Some(url) = profile.record().profile().and_then(|p| p.picture()) {
        url
    } else {
        ProfilePic::no_pfp_url()
    }
}

fn display_name_widget(
    display_name: DisplayName<'_>,
    add_placeholder_space: bool,
) -> impl egui::Widget + '_ {
    move |ui: &mut egui::Ui| match display_name {
        DisplayName::One(n) => {
            let name_response =
                ui.label(RichText::new(n).text_style(NotedeckTextStyle::Heading3.text_style()));
            if add_placeholder_space {
                ui.add_space(16.0);
            }
            name_response
        }

        DisplayName::Both {
            display_name,
            username,
        } => {
            ui.label(
                RichText::new(display_name).text_style(NotedeckTextStyle::Heading3.text_style()),
            );

            ui.label(
                RichText::new(format!("@{}", username))
                    .size(12.0)
                    .color(colors::MID_GRAY),
            )
        }
    }
}

fn about_section_widget<'a>(profile: &'a ProfileRecord<'a>) -> impl egui::Widget + 'a {
    |ui: &mut egui::Ui| {
        if let Some(about) = profile.record().profile().and_then(|p| p.about()) {
            ui.label(about)
        } else {
            // need any Response so we dont need an Option
            ui.allocate_response(egui::Vec2::ZERO, egui::Sense::hover())
        }
    }
}
