use maud::{Markup, Render, html};
use tracing::instrument;

use super::Page;
use super::components::Pagination;
use super::{ActionTarget, Note, NoteActions, NoteAuthor, NoteAuthorHandle};
use crate::activitypub::feed::FeedEntry;
use crate::activitypub::notes::NoteReactions;
use crate::registration::RegistrationMode;

pub const FEED_PAGE_SIZE: usize = 20;

/// The landing page for anonymous visitors: explains what Frogue is, shows
/// instance statistics, and indicates whether registration is open.
pub(super) struct Landing {
    pub user_count: u64,
    pub repository_count: u64,
    pub registration_mode: RegistrationMode,
}

impl Render for Landing {
    fn render(&self) -> Markup {
        html! {
            section class="landing-intro" {
                h2 { "What is Frogue?" }
                p {
                    "Frogue is a " em { "software forge" } ". It's a place to host and collaborate on software projects,"
                    "track issues, and review patch proposals. It speaks " a href="https://activitypub.rocks/" { "ActivityPub" }
                    " and " a href="https://forgefed.org/" { "ForgeFed" } ", so it can federate with other forges and ActivityPub clients."
                }
            }

            section class="landing-stats" {
                h2 { "This instance" }
                ul {
                    li { strong { (self.user_count) } " users" }
                    li { strong { (self.repository_count) } " public repositories" }
                }
            }

            section class="landing-registration" {
                h2 { "Join" }
                @match self.registration_mode {
                    RegistrationMode::Open => {
                        p {
                            "Registrations are open. "
                            a href="/register" { "Create an account" }
                            " or "
                            a href="/login" { "log in" }
                            "."
                        }
                    }
                    RegistrationMode::InviteOnly => {
                        p {
                            "Registrations are invite-only. Ask an existing user for an invite link, "
                            "then "
                            a href="/register" { "register" }
                            "."
                        }
                    }
                }
            }
        }
    }
}

/// The body of the index page: when a user is logged in and federation is
/// enabled, show their home feed. Otherwise show the landing page.
pub(super) struct Index {
    pub entries: Vec<FeedEntry>,
    pub public_url: String,
    pub viewer_username: Option<String>,
    pub page: u64,
    pub landing: Landing,
}

impl Render for Index {
    fn render(&self) -> Markup {
        if self.viewer_username.is_none() {
            // Anonymous visitor: show the landing page
            self.landing.render()
        } else if self.entries.is_empty() {
            // Empty feed - show a message
            html! {
                p { "Your feed is empty. Follow some remote users or post a note!" }
            }
        } else {
            // Logged in with feed entries
            html! {
                div class="feed" {
                    @for entry in &self.entries {
                        (render_feed_entry(entry, &self.public_url, &self.viewer_username))
                    }
                }
                (Pagination {
                    current_page: self.page,
                    has_next: self.entries.len() == FEED_PAGE_SIZE,
                    base_url: "/".to_string(),
                })
            }
        }
    }
}

/// Render a single feed entry.
fn render_feed_entry(
    entry: &FeedEntry,
    public_url: &str,
    viewer_username: &Option<String>,
) -> Markup {
    let (
        permalink,
        author,
        content_html,
        timestamp,
        target,
        reacted,
        boosted,
        likes,
        announces,
        emojis,
    ) = match entry {
        FeedEntry::Local {
            id: _,
            uuid,
            content_html,
            created_at,
            author_username,
            reacted,
            boosted,
            likes,
            announces,
            emojis,
        } => {
            let permalink = format!("{public_url}/~{author_username}/notes/{uuid}");
            let author = NoteAuthor {
                display_name: author_username.clone(),
                handle: NoteAuthorHandle::Local {
                    username: author_username.clone(),
                },
            };
            let target = ActionTarget::Local {
                user: author_username.clone(),
                uuid: uuid.clone(),
            };
            (
                permalink,
                author,
                content_html.clone(),
                *created_at,
                target,
                *reacted,
                *boosted,
                *likes,
                *announces,
                emojis.clone(),
            )
        }
        FeedEntry::Remote {
            id: _,
            remote_note_id,
            object_url,
            content_html,
            published,
            author_username,
            author_host,
            reacted,
            boosted,
            likes,
            announces,
            emojis,
        } => {
            let permalink = object_url.clone();
            let author = NoteAuthor {
                display_name: author_username.clone(),
                handle: NoteAuthorHandle::Remote {
                    username: author_username.clone(),
                    host: author_host.clone(),
                },
            };
            let target = ActionTarget::Remote {
                id: *remote_note_id,
            };
            (
                permalink,
                author,
                content_html.clone(),
                *published,
                target,
                *reacted,
                *boosted,
                *likes,
                *announces,
                emojis.clone(),
            )
        }
    };

    let is_author = match entry {
        FeedEntry::Local {
            author_username, ..
        } => viewer_username.as_ref() == Some(author_username),
        FeedEntry::Remote { .. } => false,
    };

    let actions = viewer_username.as_ref().map(|_| NoteActions {
        target,
        reacted,
        boosted,
        is_author,
    });

    let permalink_url = url::Url::parse(&permalink)
        .unwrap_or_else(|_| url::Url::parse(&format!("{public_url}/unknown")).unwrap());

    let reactions = if likes == 0 && announces == 0 && emojis.is_empty() {
        None
    } else {
        Some(NoteReactions {
            likes,
            announces,
            emojis,
        })
    };

    let note = Note {
        author,
        content_html,
        created_at: super::components::DateTime::from(&timestamp),
        updated_at: None,
        permalink: permalink_url,
        reactions,
        actions,
    };

    note.render()
}

/// The index page, as seen by `viewer` - nobody, when it is `None`.
/// When logged in and federation is enabled, shows the home feed.
/// When not logged in, shows the landing page with instance stats.
#[instrument(skip(entries))]
pub fn index(
    viewer: Option<&str>,
    entries: Vec<FeedEntry>,
    public_url: &str,
    page: u64,
    user_count: u64,
    repository_count: u64,
    registration_mode: RegistrationMode,
) -> HttpResponse {
    let viewer_username = viewer.map(str::to_string);
    let title = if viewer.is_some() {
        "Home - Frogue".to_string()
    } else {
        "Frogue".to_string()
    };

    Page {
        title,
        viewer: viewer_username.clone(),
        extra_head: None,
        body: Index {
            entries,
            public_url: public_url.to_string(),
            viewer_username,
            page,
            landing: Landing {
                user_count,
                repository_count,
                registration_mode,
            },
        },
    }
    .into_response()
}

use actix_web::HttpResponse;
