use std::time::SystemTime;

use ely_browser_core::BrowserSnapshot;
use ely_design_system::colors;
use ely_domain::{ArchivedTab, BrowserTab, HistoryEntry};
use gpui::prelude::FluentBuilder;
use gpui::{
    AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div,
    px, rgb,
};
use gpui_component::{
    Sizable, StyledExt,
    button::{Button, ButtonVariants},
    scroll::ScrollableElement,
};

use super::super::{PendingHistoryDomainClear, PendingHistoryTimeClear};
use super::{ElyShell, render_canvas_surface};

impl ElyShell {
    pub(super) fn render_history_page(
        &mut self,
        snapshot: &BrowserSnapshot,
        cx: &mut Context<Self>,
    ) -> AnyElement {
        let pending_domain_clear = self.pending_history_domain_clear.clone().filter(|pending| {
            pending.matches_context(&snapshot.active_profile_id, &snapshot.active_space_id)
        });
        let pending_time_clear = self.pending_history_time_clear.clone().filter(|pending| {
            pending.matches_context(&snapshot.active_profile_id, &snapshot.active_space_id)
        });

        render_canvas_surface(
            div()
                .size_full()
                .p_8()
                .flex()
                .flex_col()
                .gap_5()
                .child(render_history_header(snapshot, cx))
                .when_some(pending_time_clear, |this, pending| {
                    this.child(render_time_clear_confirmation(&pending, cx))
                })
                .when_some(pending_domain_clear, |this, pending| {
                    this.child(render_domain_clear_confirmation(&pending, cx))
                })
                .child(render_history_list(snapshot, cx)),
        )
    }
}

fn render_history_header(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) -> AnyElement {
    div()
        .flex()
        .items_end()
        .justify_between()
        .gap_4()
        .child(
            div()
                .min_w_0()
                .flex()
                .flex_col()
                .gap_2()
                .child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child("History"))
                .child(div().text_sm().text_color(rgb(colors::MUTED)).child(format!(
                    "{} / {}",
                    snapshot.active_profile_name, snapshot.active_space_name
                ))),
        )
        .child(
            div()
                .flex()
                .items_center()
                .gap_3()
                .child(
                    div()
                        .text_xs()
                        .text_color(rgb(colors::MUTED))
                        .child(format!("{} entries", snapshot.history_entries.len())),
                )
                .when(!snapshot.history_entries.is_empty(), |this| {
                    this.child(
                        Button::new("request-clear-recent-history")
                            .danger()
                            .xsmall()
                            .label("Clear Last Hour")
                            .tooltip("Clear Recent History")
                            .on_click(cx.listener(|shell, _, _, cx| {
                                shell.request_clear_recent_history_confirmation(cx);
                            })),
                    )
                }),
        )
        .into_any_element()
}

fn render_time_clear_confirmation(
    pending: &PendingHistoryTimeClear,
    cx: &mut Context<ElyShell>,
) -> AnyElement {
    div()
        .rounded_md()
        .border_1()
        .border_color(rgb(colors::ERROR))
        .bg(rgb(colors::CANVAS_SOFT))
        .px_4()
        .py_3()
        .flex()
        .items_center()
        .justify_between()
        .gap_4()
        .child(
            div()
                .min_w_0()
                .flex()
                .flex_col()
                .gap_1()
                .child(
                    div()
                        .text_sm()
                        .font_semibold()
                        .text_color(rgb(colors::INK))
                        .child(format!("Confirm clearing {}", pending.label())),
                )
                .child(
                    div()
                        .text_xs()
                        .text_color(rgb(colors::MUTED))
                        .child("This removes recent history in the current Space."),
                ),
        )
        .child(
            div()
                .flex()
                .items_center()
                .gap_2()
                .child(
                    Button::new("cancel-clear-recent-history")
                        .ghost()
                        .xsmall()
                        .label("Cancel")
                        .on_click(cx.listener(|shell, _, _, cx| {
                            shell.cancel_clear_recent_history_confirmation(cx);
                        })),
                )
                .child(
                    Button::new("confirm-clear-recent-history")
                        .danger()
                        .xsmall()
                        .label("Clear")
                        .on_click(cx.listener(|shell, _, _, cx| {
                            shell.clear_active_space_history_for_pending_time(cx);
                        })),
                ),
        )
        .into_any_element()
}

fn render_domain_clear_confirmation(
    pending: &PendingHistoryDomainClear,
    cx: &mut Context<ElyShell>,
) -> AnyElement {
    div()
        .rounded_md()
        .border_1()
        .border_color(rgb(colors::ERROR))
        .bg(rgb(colors::CANVAS_SOFT))
        .px_4()
        .py_3()
        .flex()
        .items_center()
        .justify_between()
        .gap_4()
        .child(
            div()
                .min_w_0()
                .flex()
                .flex_col()
                .gap_1()
                .child(
                    div()
                        .text_sm()
                        .font_semibold()
                        .text_color(rgb(colors::INK))
                        .child(format!("Confirm clearing {}", pending.host())),
                )
                .child(
                    div()
                        .text_xs()
                        .text_color(rgb(colors::MUTED))
                        .child("This removes matching history in the current Space."),
                ),
        )
        .child(
            div()
                .flex()
                .items_center()
                .gap_2()
                .child(
                    Button::new("cancel-clear-history-domain")
                        .ghost()
                        .xsmall()
                        .label("Cancel")
                        .on_click(cx.listener(|shell, _, _, cx| {
                            shell.cancel_clear_history_domain_confirmation(cx);
                        })),
                )
                .child(
                    Button::new("confirm-clear-history-domain")
                        .danger()
                        .xsmall()
                        .label("Clear")
                        .on_click(cx.listener(|shell, _, _, cx| {
                            shell.clear_active_space_history_for_pending_domain(cx);
                        })),
                ),
        )
        .into_any_element()
}

fn render_history_list(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) -> AnyElement {
    if snapshot.history_entries.is_empty() {
        return div()
            .flex_1()
            .border_t_1()
            .border_color(rgb(colors::HAIRLINE))
            .pt_5()
            .text_sm()
            .text_color(rgb(colors::MUTED))
            .child("History is empty for this Space and Profile.")
            .into_any_element();
    }

    div()
        .flex_1()
        .min_h_0()
        .flex()
        .flex_col()
        .overflow_y_scrollbar()
        .border_t_1()
        .border_color(rgb(colors::HAIRLINE))
        .children(
            snapshot
                .history_entries
                .iter()
                .rev()
                .enumerate()
                .map(|(index, entry)| render_history_row(snapshot, index, entry, cx)),
        )
        .into_any_element()
}

fn render_history_row(
    snapshot: &BrowserSnapshot,
    index: usize,
    entry: &HistoryEntry,
    cx: &mut Context<ElyShell>,
) -> AnyElement {
    let url = entry.url().clone();
    let host = entry.url().host();
    let source_tab_label = history_source_tab_label(&snapshot.tabs, &snapshot.archived_tabs, entry);

    div()
        .id(SharedString::from(format!("history-{index}")))
        .py_3()
        .border_b_1()
        .border_color(rgb(colors::HAIRLINE))
        .flex()
        .items_center()
        .justify_between()
        .gap_4()
        .child(
            div()
                .min_w_0()
                .flex()
                .flex_col()
                .gap_1()
                .child(
                    div()
                        .text_sm()
                        .font_semibold()
                        .truncate()
                        .text_color(rgb(colors::INK))
                        .child(entry.title().to_string()),
                )
                .child(
                    div()
                        .flex()
                        .items_center()
                        .gap_2()
                        .text_xs()
                        .text_color(rgb(colors::MUTED))
                        .child(div().min_w_0().truncate().child(entry.url().display_url()))
                        .child(div().text_color(rgb(colors::MUTED_SOFT)).child("-"))
                        .child(
                            div()
                                .text_color(rgb(colors::MUTED_SOFT))
                                .child(visit_count_label(entry.visit_count())),
                        )
                        .child(div().text_color(rgb(colors::MUTED_SOFT)).child("-"))
                        .child(
                            div()
                                .text_color(rgb(colors::MUTED_SOFT))
                                .child(visited_at_label(entry.visited_at())),
                        )
                        .when_some(source_tab_label, |this, source_tab_label| {
                            this.child(div().text_color(rgb(colors::MUTED_SOFT)).child("-")).child(
                                div()
                                    .max_w(px(180.0))
                                    .truncate()
                                    .text_color(rgb(colors::MUTED_SOFT))
                                    .child(format!("Source: {source_tab_label}")),
                            )
                        }),
                ),
        )
        .child(
            div()
                .flex()
                .items_center()
                .gap_2()
                .child(
                    Button::new(("open-history-entry", index))
                        .ghost()
                        .xsmall()
                        .label("Open")
                        .tooltip("Open History Entry")
                        .on_click(cx.listener(move |shell, _, window, cx| {
                            shell.open_url(url.clone(), window, cx);
                        })),
                )
                .when_some(host, |this, host| {
                    this.child(
                        Button::new(("clear-history-domain", index))
                            .danger()
                            .xsmall()
                            .label("Clear Domain")
                            .tooltip("Clear Domain History")
                            .on_click(cx.listener(move |shell, _, _, cx| {
                                shell.request_clear_history_domain_confirmation(host.clone(), cx);
                            })),
                    )
                }),
        )
        .into_any_element()
}

fn visit_count_label(visit_count: u32) -> String {
    match visit_count {
        1 => "1 visit".to_string(),
        count => format!("{count} visits"),
    }
}

fn visited_at_label(visited_at: SystemTime) -> String {
    visited_at_label_for(visited_at, SystemTime::now())
}

fn visited_at_label_for(visited_at: SystemTime, now: SystemTime) -> String {
    let Ok(elapsed) = now.duration_since(visited_at) else {
        return "Just now".to_string();
    };

    let seconds = elapsed.as_secs();
    if seconds < 60 {
        return "Just now".to_string();
    }
    if seconds < 3_600 {
        return elapsed_label(seconds / 60, "min");
    }
    if seconds < 86_400 {
        return elapsed_label(seconds / 3_600, "hr");
    }
    if seconds < 604_800 {
        return elapsed_label(seconds / 86_400, "day");
    }
    "Earlier".to_string()
}

fn elapsed_label(value: u64, unit: &str) -> String {
    match value {
        1 => format!("1 {unit} ago"),
        count => format!("{count} {unit}s ago"),
    }
}

fn history_source_tab_label(
    tabs: &[BrowserTab],
    archived_tabs: &[ArchivedTab],
    entry: &HistoryEntry,
) -> Option<String> {
    tabs.iter()
        .find(|tab| tab.id() == entry.source_tab_id())
        .or_else(|| {
            archived_tabs.iter().map(ArchivedTab::tab).find(|tab| tab.id() == entry.source_tab_id())
        })
        .map(|tab| tab.title().to_string())
}

#[cfg(test)]
mod tests {
    use std::{
        error::Error,
        time::{Duration, UNIX_EPOCH},
    };

    use ely_domain::{
        ArchiveSource, ArchivedTab, BrowserTab, HistoryEntry, ProfileId, SpaceId, TabId, UrlText,
    };

    use super::{history_source_tab_label, visited_at_label_for};

    #[test]
    fn visited_at_label_formats_recent_visit() {
        let now = UNIX_EPOCH + Duration::from_secs(120);

        assert_eq!(visited_at_label_for(now - Duration::from_secs(20), now), "Just now");
    }

    #[test]
    fn visited_at_label_formats_minutes_hours_days_and_older_visits() {
        let now = UNIX_EPOCH + Duration::from_secs(900_000);

        assert_eq!(visited_at_label_for(now - Duration::from_secs(120), now), "2 mins ago");
        assert_eq!(visited_at_label_for(now - Duration::from_secs(10_800), now), "3 hrs ago");
        assert_eq!(visited_at_label_for(now - Duration::from_secs(345_600), now), "4 days ago");
        assert_eq!(visited_at_label_for(now - Duration::from_secs(691_200), now), "Earlier");
    }

    #[test]
    fn history_source_tab_label_uses_open_tab_title() -> Result<(), Box<dyn Error>> {
        let profile_id = ProfileId::new();
        let space_id = SpaceId::new();
        let tab_id = TabId::new();
        let tab = BrowserTab::new(
            tab_id,
            space_id.clone(),
            profile_id.clone(),
            "Research Brief",
            UrlText::parse("https://example.com/research")?,
        );
        let entry = HistoryEntry::new(
            profile_id,
            space_id,
            tab.id().clone(),
            "example.com",
            UrlText::parse("https://example.com/research")?,
            None,
            UNIX_EPOCH,
        );

        assert_eq!(
            history_source_tab_label(&[tab], &[], &entry),
            Some("Research Brief".to_string())
        );
        Ok(())
    }

    #[test]
    fn history_source_tab_label_uses_archived_tab_title() -> Result<(), Box<dyn Error>> {
        let profile_id = ProfileId::new();
        let space_id = SpaceId::new();
        let tab_id = TabId::new();
        let tab = BrowserTab::new(
            tab_id,
            space_id.clone(),
            profile_id.clone(),
            "Closed Research",
            UrlText::parse("https://example.com/closed")?,
        );
        let entry = HistoryEntry::new(
            profile_id,
            space_id,
            tab.id().clone(),
            "example.com",
            UrlText::parse("https://example.com/closed")?,
            None,
            UNIX_EPOCH,
        );
        let archived_tab = ArchivedTab::new(tab, ArchiveSource::ManualClose);

        assert_eq!(
            history_source_tab_label(&[], &[archived_tab], &entry),
            Some("Closed Research".to_string())
        );
        Ok(())
    }
}
