use super::Message;
use cosmic::app::Task;
use cosmic::iced::widget::scrollable::{self, AbsoluteOffset};
use cosmic::iced::{Alignment, Length};
use cosmic::{theme, widget, Element};
use rakuos_packages::NativeApp;

pub(super) fn app_grid_page<'a>(
    title: &'static str,
    apps: &'a [NativeApp],
    visible_count: usize,
    loading: bool,
    status: &'a str,
    width: usize,
) -> Element<'a, Message> {
    if loading {
        return loading_page("Loading apps...");
    }

    let spacing = theme::active().cosmic().spacing;
    let mut column = widget::column::with_capacity(3)
        .padding([0, spacing.space_s, spacing.space_m, spacing.space_s])
        .spacing(spacing.space_xxs)
        .width(Length::Fill);

    column = column.push(
        widget::row::with_children(vec![
            widget::text::title2(title).into(),
            widget::space::horizontal().width(Length::Fill).into(),
            if apps.is_empty() {
                widget::space::horizontal().width(Length::Shrink).into()
            } else {
                widget::text::caption(format!(
                    "{} of {} apps",
                    visible_count.min(apps.len()),
                    apps.len()
                ))
                .into()
            },
        ])
        .align_y(Alignment::Center),
    );
    if apps.is_empty() {
        column = column.push(widget::text::body(if status.is_empty() {
            "No apps found."
        } else {
            status
        }));
    } else {
        let visible = visible_count.min(apps.len()).max(1);
        column = column.push(app_grid(&apps[..visible], width));
        if visible < apps.len() {
            column = column.push(
                widget::container(
                    widget::button::standard(format!("Load more ({}/{})", visible, apps.len()))
                        .on_press(Message::LoadMoreApps),
                )
                .width(Length::Fill)
                .center_x(Length::Fill),
            );
        }
    }
    column.into()
}

pub(super) fn loading_page(text: &'static str) -> Element<'static, Message> {
    widget::container(
        widget::column::with_children(vec![widget::text::body(text).into()])
            .spacing(12)
            .align_x(Alignment::Center),
    )
    .width(Length::Fill)
    .height(Length::Fill)
    .center_x(Length::Fill)
    .center_y(Length::Fill)
    .into()
}

pub(super) fn message_page(title: &'static str, message: &'static str) -> Element<'static, Message> {
    let spacing = theme::active().cosmic().spacing;
    widget::column::with_children(vec![
        widget::text::title2(title).into(),
        widget::text::body(message).into(),
    ])
    .padding([0, spacing.space_s, spacing.space_m, spacing.space_s])
    .spacing(spacing.space_xxs)
    .into()
}

pub(super) fn app_grid(apps: &[NativeApp], width: usize) -> Element<'_, Message> {
    let spacing = theme::active().cosmic().spacing;
    let GridMetrics {
        cols,
        item_width,
        column_spacing,
    } = GridMetrics::new(width, 320 + 2 * spacing.space_s as usize, spacing.space_xxs);

    let mut grid = widget::grid();
    let mut col = 0;
    for app in apps {
        if col >= cols {
            grid = grid.insert_row();
            col = 0;
        }
        let id = app.id.clone();
        grid = grid.push(widget::mouse_area(app_card(app, item_width)).on_press(Message::SelectApp(id)));
        col += 1;
    }
    grid.column_spacing(column_spacing)
        .row_spacing(column_spacing)
        .into()
}

pub(crate) fn app_icon<'a>(path: &str, url: &str, size: u16) -> Element<'a, Message> {
    if let Some(icon) = local_icon(path, size) {
        icon
    } else if url.starts_with("http://") || url.starts_with("https://") {
        widget::image(url.to_string())
            .width(Length::Fixed(size as f32))
            .height(Length::Fixed(size as f32))
            .into()
    } else {
        widget::icon::from_name("package-x-generic")
            .size(size)
            .into()
    }
}

fn local_icon<'a>(path: &str, size: u16) -> Option<Element<'a, Message>> {
    if path.is_empty() || !std::path::Path::new(path).exists() {
        return None;
    }

    let bytes = std::fs::read(path).ok()?;
    let handle = if is_svg_icon(path, &bytes) {
        widget::icon::from_svg_bytes(bytes)
    } else {
        widget::icon::from_raster_bytes(bytes)
    };

    Some(widget::icon::icon(handle).size(size).into())
}

fn is_svg_icon(path: &str, bytes: &[u8]) -> bool {
    if std::path::Path::new(path)
        .extension()
        .and_then(|ext| ext.to_str())
        .is_some_and(|ext| ext.eq_ignore_ascii_case("svg"))
    {
        return true;
    }

    let sniff_len = bytes.len().min(512);
    let sniff = String::from_utf8_lossy(&bytes[..sniff_len]).to_lowercase();
    sniff.contains("<svg") || sniff.contains("<?xml")
}

pub(crate) fn scroll_to_top(id: widget::Id) -> Task<Message> {
    scrollable::scroll_to(id, AbsoluteOffset::default())
}

fn app_card(app: &NativeApp, width: usize) -> Element<'_, Message> {
    let spacing = theme::active().cosmic().spacing;
    widget::container(
        widget::row::with_children(vec![
            app_icon(&app.icon_path, &app.icon_url, super::super::ICON_SIZE_CARD),
            widget::column::with_children(vec![
                widget::text::body(&app.name)
                    .height(Length::Fixed(20.0))
                    .into(),
                widget::text::caption(&app.summary)
                    .height(Length::Fixed(32.0))
                    .into(),
            ])
            .into(),
        ])
        .align_y(Alignment::Center)
        .spacing(spacing.space_s),
    )
    .align_y(Alignment::Center)
    .width(Length::Fixed(width as f32))
    .height(Length::Fixed(56.0 + (spacing.space_xxs as f32) * 2.0))
    .padding([spacing.space_xxs, spacing.space_s])
    .class(theme::Container::Card)
    .into()
}

struct GridMetrics {
    cols: usize,
    item_width: usize,
    column_spacing: u16,
}

impl GridMetrics {
    fn new(width: usize, min_width: usize, column_spacing: u16) -> Self {
        let width_m1 = width.saturating_sub(min_width);
        let cols_m1 = width_m1 / (min_width + column_spacing as usize);
        let cols = cols_m1 + 1;
        let item_width = width
            .saturating_sub(cols_m1 * column_spacing as usize)
            .checked_div(cols)
            .unwrap_or(0);
        Self {
            cols,
            item_width,
            column_spacing,
        }
    }
}
