use egui::{vec2, Color32, InnerResponse, Layout, Margin, Separator, Stroke, Widget};
use tracing::info;

use crate::{
    accounts::AccountsRoute,
    colors,
    column::{Column, Columns},
    imgcache::ImageCache,
    route::Route,
    support::Support,
    user_account::UserAccount,
    Damus,
};

use super::{
    anim::{AnimationHelper, ICON_EXPANSION_MULTIPLE},
    profile::preview::get_account_url,
    ProfilePic, View,
};

pub static SIDE_PANEL_WIDTH: f32 = 64.0;
static ICON_WIDTH: f32 = 40.0;

pub struct DesktopSidePanel<'a> {
    ndb: &'a nostrdb::Ndb,
    img_cache: &'a mut ImageCache,
    selected_account: Option<&'a UserAccount>,
}

impl View for DesktopSidePanel<'_> {
    fn ui(&mut self, ui: &mut egui::Ui) {
        self.show(ui);
    }
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum SidePanelAction {
    Panel,
    Account,
    Settings,
    Columns,
    ComposeNote,
    Search,
    ExpandSidePanel,
    Support,
}

pub struct SidePanelResponse {
    pub response: egui::Response,
    pub action: SidePanelAction,
}

impl SidePanelResponse {
    fn new(action: SidePanelAction, response: egui::Response) -> Self {
        SidePanelResponse { action, response }
    }
}

impl<'a> DesktopSidePanel<'a> {
    pub fn new(
        ndb: &'a nostrdb::Ndb,
        img_cache: &'a mut ImageCache,
        selected_account: Option<&'a UserAccount>,
    ) -> Self {
        Self {
            ndb,
            img_cache,
            selected_account,
        }
    }

    pub fn show(&mut self, ui: &mut egui::Ui) -> SidePanelResponse {
        let mut frame = egui::Frame::none().inner_margin(Margin::same(8.0));

        if !ui.visuals().dark_mode {
            frame = frame.fill(colors::ALMOST_WHITE);
        }

        frame.show(ui, |ui| self.show_inner(ui)).inner
    }

    fn show_inner(&mut self, ui: &mut egui::Ui) -> SidePanelResponse {
        let dark_mode = ui.ctx().style().visuals.dark_mode;

        let inner = ui
            .vertical(|ui| {
                let top_resp = ui
                    .with_layout(Layout::top_down(egui::Align::Center), |ui| {
                        let expand_resp = ui.add(expand_side_panel_button());
                        ui.add_space(28.0);
                        let is_interactive = self
                            .selected_account
                            .is_some_and(|s| s.secret_key.is_some());
                        let compose_resp = ui.add(compose_note_button(is_interactive));
                        let compose_resp = if is_interactive {
                            compose_resp
                        } else {
                            compose_resp.on_hover_cursor(egui::CursorIcon::NotAllowed)
                        };
                        let search_resp = ui.add(search_button());
                        let column_resp = ui.add(add_column_button(dark_mode));

                        ui.add(Separator::default().horizontal().spacing(8.0).shrink(4.0));

                        if expand_resp.clicked() {
                            Some(InnerResponse::new(
                                SidePanelAction::ExpandSidePanel,
                                expand_resp,
                            ))
                        } else if compose_resp.clicked() {
                            Some(InnerResponse::new(
                                SidePanelAction::ComposeNote,
                                compose_resp,
                            ))
                        } else if search_resp.clicked() {
                            Some(InnerResponse::new(SidePanelAction::Search, search_resp))
                        } else if column_resp.clicked() {
                            Some(InnerResponse::new(SidePanelAction::Columns, column_resp))
                        } else {
                            None
                        }
                    })
                    .inner;

                let (pfp_resp, bottom_resp) = ui
                    .with_layout(Layout::bottom_up(egui::Align::Center), |ui| {
                        let pfp_resp = self.pfp_button(ui);
                        let settings_resp = ui.add(settings_button(dark_mode));

                        let support_resp = ui.add(support_button());

                        let optional_inner = if pfp_resp.clicked() {
                            Some(egui::InnerResponse::new(
                                SidePanelAction::Account,
                                pfp_resp.clone(),
                            ))
                        } else if settings_resp.clicked() || settings_resp.hovered() {
                            Some(egui::InnerResponse::new(
                                SidePanelAction::Settings,
                                settings_resp,
                            ))
                        } else if support_resp.clicked() {
                            Some(egui::InnerResponse::new(
                                SidePanelAction::Support,
                                support_resp,
                            ))
                        } else {
                            None
                        };

                        (pfp_resp, optional_inner)
                    })
                    .inner;

                if let Some(bottom_inner) = bottom_resp {
                    bottom_inner
                } else if let Some(top_inner) = top_resp {
                    top_inner
                } else {
                    egui::InnerResponse::new(SidePanelAction::Panel, pfp_resp)
                }
            })
            .inner;

        SidePanelResponse::new(inner.inner, inner.response)
    }

    fn pfp_button(&mut self, ui: &mut egui::Ui) -> egui::Response {
        let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
        let helper = AnimationHelper::new(ui, "pfp-button", vec2(max_size, max_size));

        let min_pfp_size = ICON_WIDTH;
        let cur_pfp_size = helper.scale_1d_pos(min_pfp_size);

        let txn = nostrdb::Transaction::new(self.ndb).expect("should be able to create txn");
        let profile_url = get_account_url(&txn, self.ndb, self.selected_account);

        let widget = ProfilePic::new(self.img_cache, profile_url).size(cur_pfp_size);

        ui.put(helper.get_animation_rect(), widget);

        helper.take_animation_response()
    }

    pub fn perform_action(columns: &mut Columns, support: &mut Support, action: SidePanelAction) {
        let router = columns.get_first_router();
        match action {
            SidePanelAction::Panel => {} // TODO
            SidePanelAction::Account => {
                if router
                    .routes()
                    .iter()
                    .any(|&r| r == Route::Accounts(AccountsRoute::Accounts))
                {
                    // return if we are already routing to accounts
                    router.go_back();
                } else {
                    router.route_to(Route::accounts());
                }
            }
            SidePanelAction::Settings => {
                if router.routes().iter().any(|&r| r == Route::Relays) {
                    // return if we are already routing to accounts
                    router.go_back();
                } else {
                    router.route_to(Route::relays());
                }
            }
            SidePanelAction::Columns => {
                if router
                    .routes()
                    .iter()
                    .any(|&r| matches!(r, Route::AddColumn(_)))
                {
                    router.go_back();
                } else {
                    columns.new_column_picker();
                }
            }
            SidePanelAction::ComposeNote => {
                if router.routes().iter().any(|&r| r == Route::ComposeNote) {
                    router.go_back();
                } else {
                    router.route_to(Route::ComposeNote);
                }
            }
            SidePanelAction::Search => {
                // TODO
                info!("Clicked search button");
            }
            SidePanelAction::ExpandSidePanel => {
                // TODO
                info!("Clicked expand side panel button");
            }
            SidePanelAction::Support => {
                if router.routes().iter().any(|&r| r == Route::Support) {
                    router.go_back();
                } else {
                    support.refresh();
                    router.route_to(Route::Support);
                }
            }
        }
    }
}

fn settings_button(dark_mode: bool) -> impl Widget {
    let _ = dark_mode;
    |ui: &mut egui::Ui| {
        let img_size = 24.0;
        let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
        let img_data = egui::include_image!("../../assets/icons/settings_dark_4x.png");
        let img = egui::Image::new(img_data).max_width(img_size);

        let helper = AnimationHelper::new(ui, "settings-button", vec2(max_size, max_size));

        let cur_img_size = helper.scale_1d_pos(img_size);
        img.paint_at(
            ui,
            helper
                .get_animation_rect()
                .shrink((max_size - cur_img_size) / 2.0),
        );

        helper.take_animation_response()
    }
}

fn add_column_button(dark_mode: bool) -> impl Widget {
    let _ = dark_mode;
    move |ui: &mut egui::Ui| {
        let img_size = 24.0;
        let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget

        let img_data = egui::include_image!("../../assets/icons/add_column_dark_4x.png");

        let img = egui::Image::new(img_data).max_width(img_size);

        let helper = AnimationHelper::new(ui, "add-column-button", vec2(max_size, max_size));

        let cur_img_size = helper.scale_1d_pos(img_size);
        img.paint_at(
            ui,
            helper
                .get_animation_rect()
                .shrink((max_size - cur_img_size) / 2.0),
        );

        helper.take_animation_response()
    }
}

fn compose_note_button(interactive: bool) -> impl Widget {
    move |ui: &mut egui::Ui| -> egui::Response {
        let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget

        let min_outer_circle_diameter = 40.0;
        let min_plus_sign_size = 14.0; // length of the plus sign
        let min_line_width = 2.25; // width of the plus sign

        let helper = if interactive {
            AnimationHelper::new(ui, "note-compose-button", vec2(max_size, max_size))
        } else {
            AnimationHelper::no_animation(ui, vec2(max_size, max_size))
        };

        let painter = ui.painter_at(helper.get_animation_rect());

        let use_background_radius = helper.scale_radius(min_outer_circle_diameter);
        let use_line_width = helper.scale_1d_pos(min_line_width);
        let use_edge_circle_radius = helper.scale_radius(min_line_width);

        let fill_color = if interactive {
            colors::PINK
        } else {
            ui.visuals().noninteractive().bg_fill
        };

        painter.circle_filled(helper.center(), use_background_radius, fill_color);

        let min_half_plus_sign_size = min_plus_sign_size / 2.0;
        let north_edge = helper.scale_from_center(0.0, min_half_plus_sign_size);
        let south_edge = helper.scale_from_center(0.0, -min_half_plus_sign_size);
        let west_edge = helper.scale_from_center(-min_half_plus_sign_size, 0.0);
        let east_edge = helper.scale_from_center(min_half_plus_sign_size, 0.0);

        painter.line_segment(
            [north_edge, south_edge],
            Stroke::new(use_line_width, Color32::WHITE),
        );
        painter.line_segment(
            [west_edge, east_edge],
            Stroke::new(use_line_width, Color32::WHITE),
        );
        painter.circle_filled(north_edge, use_edge_circle_radius, Color32::WHITE);
        painter.circle_filled(south_edge, use_edge_circle_radius, Color32::WHITE);
        painter.circle_filled(west_edge, use_edge_circle_radius, Color32::WHITE);
        painter.circle_filled(east_edge, use_edge_circle_radius, Color32::WHITE);

        helper.take_animation_response()
    }
}

fn search_button() -> impl Widget {
    |ui: &mut egui::Ui| -> egui::Response {
        let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
        let min_line_width_circle = 1.5; // width of the magnifying glass
        let min_line_width_handle = 1.5;
        let helper = AnimationHelper::new(ui, "search-button", vec2(max_size, max_size));

        let painter = ui.painter_at(helper.get_animation_rect());

        let cur_line_width_circle = helper.scale_1d_pos(min_line_width_circle);
        let cur_line_width_handle = helper.scale_1d_pos(min_line_width_handle);
        let min_outer_circle_radius = helper.scale_radius(15.0);
        let cur_outer_circle_radius = helper.scale_1d_pos(min_outer_circle_radius);
        let min_handle_length = 7.0;
        let cur_handle_length = helper.scale_1d_pos(min_handle_length);

        let circle_center = helper.scale_from_center(-2.0, -2.0);

        let handle_vec = vec2(
            std::f32::consts::FRAC_1_SQRT_2,
            std::f32::consts::FRAC_1_SQRT_2,
        );

        let handle_pos_1 = circle_center + (handle_vec * (cur_outer_circle_radius - 3.0));
        let handle_pos_2 =
            circle_center + (handle_vec * (cur_outer_circle_radius + cur_handle_length));

        let circle_stroke = Stroke::new(cur_line_width_circle, colors::MID_GRAY);
        let handle_stroke = Stroke::new(cur_line_width_handle, colors::MID_GRAY);

        painter.line_segment([handle_pos_1, handle_pos_2], handle_stroke);
        painter.circle(
            circle_center,
            min_outer_circle_radius,
            ui.style().visuals.widgets.inactive.weak_bg_fill,
            circle_stroke,
        );

        helper.take_animation_response()
    }
}

// TODO: convert to responsive button when expanded side panel impl is finished
fn expand_side_panel_button() -> impl Widget {
    |ui: &mut egui::Ui| -> egui::Response {
        let img_size = 40.0;
        let img_data = egui::include_image!("../../assets/damus_rounded_80.png");
        let img = egui::Image::new(img_data).max_width(img_size);

        ui.add(img)
    }
}

fn support_button() -> impl Widget {
    |ui: &mut egui::Ui| -> egui::Response {
        let img_size = 16.0;

        let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
        let img_data = egui::include_image!("../../assets/icons/help_icon_dark_4x.png");
        let img = egui::Image::new(img_data).max_width(img_size);

        let helper = AnimationHelper::new(ui, "help-button", vec2(max_size, max_size));

        let cur_img_size = helper.scale_1d_pos(img_size);
        img.paint_at(
            ui,
            helper
                .get_animation_rect()
                .shrink((max_size - cur_img_size) / 2.0),
        );

        helper.take_animation_response()
    }
}

mod preview {

    use egui_extras::{Size, StripBuilder};

    use crate::{
        test_data,
        ui::{Preview, PreviewConfig},
    };

    use super::*;

    pub struct DesktopSidePanelPreview {
        app: Damus,
    }

    impl DesktopSidePanelPreview {
        fn new() -> Self {
            let mut app = test_data::test_app();
            app.columns.add_column(Column::new(vec![Route::accounts()]));
            DesktopSidePanelPreview { app }
        }
    }

    impl View for DesktopSidePanelPreview {
        fn ui(&mut self, ui: &mut egui::Ui) {
            StripBuilder::new(ui)
                .size(Size::exact(SIDE_PANEL_WIDTH))
                .sizes(Size::remainder(), 0)
                .clip(true)
                .horizontal(|mut strip| {
                    strip.cell(|ui| {
                        let mut panel = DesktopSidePanel::new(
                            &self.app.ndb,
                            &mut self.app.img_cache,
                            self.app.accounts.get_selected_account(),
                        );
                        let response = panel.show(ui);

                        DesktopSidePanel::perform_action(
                            &mut self.app.columns,
                            &mut self.app.support,
                            response.action,
                        );
                    });
                });
        }
    }

    impl Preview for DesktopSidePanel<'_> {
        type Prev = DesktopSidePanelPreview;

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