use leptos::*;
use leptos_router::*;
use crate::app::{
    models::{Post, PostMetadata},
    components::{
        Loading, Nav, ReadingTime, DateTime
    }
};

#[server]
pub async fn get_posts(
    tag: Option<String>
) -> Result<Vec<Post>, ServerFnError> {
    leptos_actix::extract(
        |data: actix_web::web::Data<crate::app::models::Data>| async move {
            let data = data.into_inner();
            let default = vec![];
            let posts = match tag {
                Some(tag) => data.posts_by_tag.get(&tag).unwrap_or(&default),
                None => &data.posts
            };
            posts.iter()
                .map(|post| {
                    Post {
                        metadata: post.metadata.clone(),
                        content: post.content.clone(),
                    }
                })
                .collect::<Vec<_>, >()
        },
    ).await
}

#[server]
pub async fn get_post(
    slug: String
) -> Result<Post, ServerFnError> {
    leptos_actix::extract(
        |data: actix_web::web::Data<crate::app::models::Data>| async move {
            let data = data.into_inner();
            data.posts_by_slug.get(&slug)
                .and_then(|post| Some(Post {
                    metadata: post.metadata.clone(),
                    content: post.content.clone(),
                }))
        },
    )
    .await
    .and_then(|post| post.ok_or_else(|| ServerFnError::ServerError("Post not found".to_string())))
}

#[component]
pub fn PostHeader(
    metadata: PostMetadata,
    full_element: bool
) -> impl IntoView {
    let class = if full_element {
        "metadata border-b-2 pb-5"
    } else {
        "metadata"
    };

    let title = if full_element {
        view! { <>{metadata.title.clone()}</> }.into_view()
    } else {
        view! { <A href=format!("/posts/{}", metadata.slug.clone())>{metadata.title.clone()}</A> }
    };

    view! {
        <div class=class>
            <h2>{title}</h2>
            <p>{metadata.description.clone()}</p>
            <DateTime datetime=metadata.date />
            <ReadingTime time=metadata.reading_time />
            <PostTags tags=metadata.tags.clone()/>
        </div>
    }
}

#[component]
pub fn PostTags(
    tags: Vec<String>
) -> impl IntoView {
    view! {
        <div class="tags_list">
        {
            tags.into_iter().map(|tag| view! { <A class="tag" href=format!("/posts?tag={}", tag)>{tag}</A>}).collect_view()
        }
        </div>
    }
}

#[component]
pub fn PostListCard(
    post: Post
) -> impl IntoView {
    view! {
        <div>
            <img src={post.metadata.image_path.clone()} alt=format!("Image {}", post.metadata.title)/>

            {
                if post.metadata.draft {
                    Some(view!{
                        <div class="warning">
                            <leptos_icons::Icon icon=icondata::IoConstruct/>
                        </div>
                    })
                } else {
                    None
                }
            }

            <PostHeader metadata=post.metadata.clone() full_element=false />
        </div>
    }
}

#[component]
pub fn PostList() -> impl IntoView {
    let query = use_query_map();
    let tag = move || query.with(|query| query.get("tag").cloned());
    let posts = create_resource(move || tag(), move |_| get_posts(tag()));

    let posts_view = move || {
        posts.and_then(|posts| {
            posts.iter()
                .map(|post| view! { <PostListCard post=post.clone() /> })
                .collect_view()
        })
    };

    view! {
        <Suspense fallback=move || view! { <Loading title="Chargement des posts...".to_string() /> }>
            <Nav/>
            <main class="posts">
                <div class="posts__cards">{posts_view}</div>
            </main>
        </Suspense>
    }
}

#[component]
pub fn PostElement() -> impl IntoView {
    let params = use_params_map();
    let slug = move || params.with(|params| params.get("slug").cloned().unwrap_or_default());

    let post = create_resource(|| (), move |_| get_post(slug()));

    let post_view = move || {
        post.and_then(|post| {
            view! {
                <>
                    {
                        if post.metadata.draft {
                            Some(view!{
                                <div class="bg-warning text-on_warning dark:bg-dark_warning dark:text-dart_on_warning rounded-md p-5 mb-5">
                                    r#"
                                    L'article est en cours d'écriture. La formulation peut ne pas être exacte et les phrases peuvent contenir des fautes.
                                    "#
                                </div>
                            })
                        } else {
                            None
                        }
                    }

                    <PostHeader metadata=post.metadata.clone() full_element=true />

                    <div inner_html={post.content.clone()}></div>
                </>
            }
        })
    };

    view! {
        <Suspense fallback=move || view! { <Loading title="Chargement du post...".to_string() /> }>
            <Nav/>
            <main class="post">
                {post_view}
                <script>load();</script>
            </main>
        </Suspense>
    }
}