use std::ops::Range;

use serde::{Deserialize, Serialize};
use yew::prelude::*;
use yew_router::components::Link;

use crate::Route;

const ELLIPSIS: &str = "\u{02026}";

#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct PageQuery {
  pub page: u8,
}

#[derive(Clone, Debug, PartialEq, Eq, Properties)]
pub struct PropsNavigation {
  pub page: u8,
  pub total_pages: u8,
  pub route_to_page: Route,
}

#[function_component]
pub fn RelNavButtons(props: &PropsNavigation) -> Html {
  let PropsNavigation {
    page,
    total_pages,
    route_to_page: to,
  } = props.clone();

  html! {
    <>
      <Link<Route, PageQuery>
        classes={classes!("pagination-previous")}
        disabled={page==1}
        query={Some(PageQuery{page: page - 1})}
        to={to.clone()}
      >
        { "Previous" }
      </Link<Route, PageQuery>>
      <Link<Route, PageQuery>
        classes={classes!("pagination-next")}
        disabled={page==total_pages}
        query={Some(PageQuery{page: page + 1})}
        {to}
      >
        { "Next page" }
      </Link<Route, PageQuery>>
    </>
  }
}

#[derive(Properties, Clone, Debug, PartialEq, Eq)]
pub struct RenderLinksProps {
  range: Range<u8>,
  len: usize,
  max_links: usize,
  props: PropsNavigation,
}

#[function_component]
pub fn RenderLinks(props: &RenderLinksProps) -> Html {
  let RenderLinksProps {
    range,
    len,
    max_links,
    props,
  } = props.clone();

  let mut range = range;

  if len > max_links {
    let last_link =
      html! {<RenderLink to_page={range.next_back().unwrap()} props={props.clone()} />};
    let links = range
      .take(max_links - 2)
      .map(|page| html! {<RenderLink to_page={page} props={props.clone()} />});
    html! {
      <>
        { for links }
        <li><span class="pagination-ellipsis">{ ELLIPSIS }</span></li>
        { last_link }
      </>
    }
  } else {
    html! {
      for page in range {
        <RenderLink to_page={page} props={props.clone()} />
      }
    }
  }
}

#[derive(Properties, Clone, Debug, PartialEq, Eq)]
pub struct RenderLinkProps {
  to_page: u8,
  props: PropsNavigation,
}

#[function_component]
pub fn RenderLink(props: &RenderLinkProps) -> Html {
  let RenderLinkProps { to_page, props } = props.clone();

  let PropsNavigation {
    page,
    route_to_page,
    ..
  } = props;

  let is_current_class = if to_page == page { "is-current" } else { "" };

  html! {
    <li>
      <Link<Route, PageQuery>
        classes={classes!("pagination-link", is_current_class)}
        to={route_to_page}
        query={Some(PageQuery{page: to_page})}
      >
        { to_page }
      </Link<Route, PageQuery>>
    </li>
  }
}

#[function_component]
pub fn Links(props: &PropsNavigation) -> Html {
  const LINKS_PER_SIDE: usize = 3;

  let PropsNavigation {
    page, total_pages, ..
  } = *props;

  let pages_prev = page.checked_sub(1).unwrap_or_default() as usize;
  let pages_next = (total_pages - page) as usize;

  let links_left = LINKS_PER_SIDE.min(pages_prev)
      // if there are less than `LINKS_PER_SIDE` to the right, we add some more on the left.
      + LINKS_PER_SIDE.checked_sub(pages_next).unwrap_or_default();
  let links_right = 2 * LINKS_PER_SIDE - links_left;

  html! {
    <>
      <RenderLinks range={ 1..page } len={pages_prev} max_links={links_left} props={props.clone()} />
      <RenderLink to_page={page} props={props.clone()} />
      <RenderLinks range={ page + 1..total_pages + 1 } len={pages_next} max_links={links_right} props={props.clone()} />
    </>
  }
}

#[function_component]
pub fn Pagination(props: &PropsNavigation) -> Html {
  html! {
    <nav class="pagination is-right" role="navigation" aria-label="pagination">
      <RelNavButtons ..{props.clone()} />
      <ul class="pagination-list">
        <Links ..{props.clone()} />
      </ul>
    </nav>
  }
}

#[function_component]
pub fn Nav() -> Html {
  let navbar_active = use_state_eq(|| false);

  let toggle_navbar = {
    let navbar_active = navbar_active.clone();

    Callback::from(move |_| {
      navbar_active.set(!*navbar_active);
    })
  };

  let active_class = if !*navbar_active { "is-active" } else { "" };

  html! {
    <nav class="navbar is-primary" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
        <h1 class="navbar-item is-size-3">{ "Yew Blog" }</h1>

        <button class={classes!("navbar-burger", "burger", active_class)}
          aria-label="menu" aria-expanded="false"
          onclick={toggle_navbar}
        >
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
        </button>
      </div>
      <div class={classes!("navbar-menu", active_class)}>
        <div class="navbar-start">
          <Link<Route> classes={classes!("navbar-item")} to={Route::Entries}> // TODO: ...
            { "Home" }
          </Link<Route>>
          <Link<Route> classes={classes!("navbar-item")} to={Route::Entries}>
            { "Posts" }
          </Link<Route>>

          <div class="navbar-item has-dropdown is-hoverable">
            <div class="navbar-link">
              { "More" }
            </div>
            <div class="navbar-dropdown">
              <Link<Route> classes={classes!("navbar-item")} to={Route::Authors}>
                { "Meet the authors" }
              </Link<Route>>
            </div>
          </div>
        </div>
      </div>
    </nav>
  }
}
