use super::components;
use crate::activitypub::notes::NoteReactions;
use maud::{Markup, PreEscaped, Render, html};
use url::Url;

/// The target for note actions (like/announce endpoints).
#[derive(Debug, Clone)]
pub enum ActionTarget {
    /// Local note: /~{user}/notes/{uuid}/react|announce
    Local { user: String, uuid: String },
    /// Remote note: /remote-notes/{id}/react|announce
    Remote { id: i64 },
}

impl ActionTarget {
    /// Get the react endpoint URL.
    pub fn react_url(&self) -> String {
        match self {
            Self::Local { user, uuid } => format!("/~{user}/notes/{uuid}/react"),
            Self::Remote { id } => format!("/remote-notes/{id}/react"),
        }
    }

    /// Get the unreact endpoint URL.
    pub fn unreact_url(&self) -> String {
        match self {
            Self::Local { user, uuid } => format!("/~{user}/notes/{uuid}/unreact"),
            Self::Remote { id } => format!("/remote-notes/{id}/unreact"),
        }
    }

    /// Get the announce endpoint URL.
    pub fn announce_url(&self) -> String {
        match self {
            Self::Local { user, uuid } => format!("/~{user}/notes/{uuid}/announce"),
            Self::Remote { id } => format!("/remote-notes/{id}/announce"),
        }
    }

    /// Get the unannounce endpoint URL.
    pub fn unannounce_url(&self) -> String {
        match self {
            Self::Local { user, uuid } => format!("/~{user}/notes/{uuid}/unannounce"),
            Self::Remote { id } => format!("/remote-notes/{id}/unannounce"),
        }
    }
}

/// Actions available for a note (react/boost state).
#[derive(Debug, Clone)]
pub struct NoteActions {
    /// The target for form actions.
    pub target: ActionTarget,
    /// Whether the viewer has reacted (liked).
    pub reacted: bool,
    /// Whether the viewer has announced (boosted).
    pub boosted: bool,
    /// Whether the viewer is the author (suppresses React, keeps Announce).
    pub is_author: bool,
}

/// A note author display.
pub struct NoteAuthor {
    pub display_name: String,
    pub handle: NoteAuthorHandle,
    pub icon_url: Option<String>,
}

impl Render for NoteAuthor {
    fn render(&self) -> Markup {
        html! {
            span class="note-author" {
                (Avatar {
                    icon_url: self.icon_url.clone(),
                    alt: self.display_name.clone(),
                })
                (self.handle)
            }
        }
    }
}

/// Avatar image with fallback to frogue logo
pub struct Avatar {
    pub icon_url: Option<String>,
    pub alt: String,
}

impl Render for Avatar {
    fn render(&self) -> Markup {
        let src = self
            .icon_url
            .as_deref()
            .unwrap_or("/static/frogue-logo.svg");
        html! {
            img.avatar src=(src) alt=(self.alt) loading="lazy" width="24" height="24" {}
        }
    }
}

/// A note author handle (local or remote).
pub enum NoteAuthorHandle {
    Local {
        username: String,
        actor_url: String,
    },
    Remote {
        username: String,
        host: String,
        actor_url: String,
    },
}

impl Render for NoteAuthorHandle {
    fn render(&self) -> Markup {
        match self {
            Self::Local {
                username,
                actor_url,
            } => {
                html! {
                    a class="note-author-handle frogue-username" href=(actor_url) {
                        "~" (username)
                    }
                }
            }
            Self::Remote {
                username,
                host,
                actor_url,
            } => {
                html! {
                    span class="note-author-handle" {
                        a href=(actor_url) {
                            "@" (username) "@" (host)
                        }
                        " "
                        span class="remote-badge" { "remote" }
                    }
                }
            }
        }
    }
}

/// A unified note component with author, content, permalink, counts, and actions.
pub struct Note {
    /// The author display info.
    pub author: NoteAuthor,
    /// The HTML content (will be rendered as-is, not escaped).
    pub content_html: String,
    /// Creation timestamp.
    pub created_at: components::DateTime,
    /// Optional update timestamp (shown as "edited").
    pub updated_at: Option<components::DateTime>,
    /// Permalink URL.
    pub permalink: Url,
    /// Reaction counts (likes, announces, emojis).
    pub reactions: Option<NoteReactions>,
    /// Viewer actions (react/boost state), None if not logged in.
    pub actions: Option<NoteActions>,
}

impl Render for Note {
    fn render(&self) -> Markup {
        html! {
            article class="note" {
                header {
                    (self.author)
                    span class="note-time" {
                        (self.created_at)
                        @if let Some(_updated_at) = &self.updated_at {
                            " "
                            span class="note-edited" { "(edited)" }
                        }
                    }
                }
                main {
                    (PreEscaped(&self.content_html))
                }
                footer {
                    a href=(self.permalink) { "permalink" }

                    @if let Some(reactions) = &self.reactions {
                        @if !reactions.is_empty() {
                            span class="note-reactions" {
                                @if reactions.likes > 0 {
                                    span class="reaction-count" { (reactions.likes) " ❤️" }
                                }
                                @if reactions.announces > 0 {
                                    span class="reaction-count" { (reactions.announces) " 🔁" }
                                }
                                @for (emoji, count) in &reactions.emojis {
                                    span class="reaction-count" { (count) " " (emoji) }
                                }
                            }
                        }
                    }

                    @if let Some(actions) = &self.actions {
                        // React button: hidden for author
                        @if !actions.is_author {
                            @if actions.reacted {
                                form method="post" action=(actions.target.unreact_url()) class="note-action-form" {
                                    button type="submit" class="react-button reacted" { "❤️ Unreact" }
                                }
                            } @else {
                                form method="post" action=(actions.target.react_url()) class="note-action-form" {
                                    button type="submit" class="react-button" { "❤️ React" }
                                }
                            }
                        }

                        // Announce button: always shown (author can announce)
                        @if actions.boosted {
                            form method="post" action=(actions.target.unannounce_url()) class="note-action-form" {
                                button type="submit" class="react-button reacted" { "🔁 Unboost" }
                            }
                        } @else {
                            form method="post" action=(actions.target.announce_url()) class="note-action-form" {
                                button type="submit" class="react-button" { "🔁 Boost" }
                            }
                        }
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::Utc;

    #[test]
    fn test_note_renders_basic_structure() {
        let note = Note {
            author: NoteAuthor {
                display_name: "Alice".to_string(),
                handle: NoteAuthorHandle::Local {
                    username: "alice".to_string(),
                    actor_url: "/~alice".to_string(),
                },
                icon_url: None,
            },
            content_html: "<p>Hello world!</p>".to_string(),
            created_at: components::DateTime::from(&Utc::now()),
            updated_at: None,
            permalink: Url::try_from("http://localhost/notes/1").expect("valid URL"),
            reactions: None,
            actions: None,
        };

        let html = note.render().into_string();
        assert!(html.contains("<article class=\"note\">"));
        assert!(html.contains("<p>Hello world!</p>"));
        assert!(html.contains("permalink"));
        assert!(html.contains("http://localhost/notes/1"));
        assert!(html.contains("~alice"));
    }

    #[test]
    fn test_note_with_reactions() {
        let note = Note {
            author: NoteAuthor {
                display_name: "Bob".to_string(),
                handle: NoteAuthorHandle::Remote {
                    username: "bob".to_string(),
                    host: "example.com".to_string(),
                    actor_url: "http://example.com/users/bob".to_string(),
                },
                icon_url: None,
            },
            content_html: "<p>Test</p>".to_string(),
            created_at: components::DateTime::from(&Utc::now()),
            updated_at: None,
            permalink: Url::try_from("http://localhost/notes/2").expect("valid URL"),
            reactions: Some(NoteReactions {
                likes: 3,
                announces: 2,
                emojis: vec![("🎉".to_string(), 1)],
            }),
            actions: None,
        };

        let html = note.render().into_string();
        assert!(html.contains("3 ❤️"));
        assert!(html.contains("2 🔁"));
        assert!(html.contains("🎉"));
    }

    #[test]
    fn test_note_with_actions_hides_react_for_author() {
        let note = Note {
            author: NoteAuthor {
                display_name: "Alice".to_string(),
                handle: NoteAuthorHandle::Local {
                    username: "alice".to_string(),
                    actor_url: "/~alice".to_string(),
                },
                icon_url: None,
            },
            content_html: "<p>My note</p>".to_string(),
            created_at: components::DateTime::from(&Utc::now()),
            updated_at: None,
            permalink: Url::try_from("http://localhost/~alice/notes/abc").expect("valid URL"),
            reactions: None,
            actions: Some(NoteActions {
                target: ActionTarget::Local {
                    user: "alice".to_string(),
                    uuid: "abc".to_string(),
                },
                reacted: false,
                boosted: false,
                is_author: true,
            }),
        };

        let html = note.render().into_string();
        // Should NOT contain React button
        assert!(!html.contains("❤️ React"));
        assert!(!html.contains("❤️ Unreact"));
        // Should contain Boost button
        assert!(html.contains("🔁 Boost"));
        assert!(html.contains("/~alice/notes/abc/announce"));
    }

    #[test]
    fn test_note_with_actions_shows_react_for_non_author() {
        let note = Note {
            author: NoteAuthor {
                display_name: "Bob".to_string(),
                handle: NoteAuthorHandle::Local {
                    username: "bob".to_string(),
                    actor_url: "/~bob".to_string(),
                },
                icon_url: None,
            },
            content_html: "<p>Bob's note</p>".to_string(),
            created_at: components::DateTime::from(&Utc::now()),
            updated_at: None,
            permalink: Url::try_from("http://localhost/~bob/notes/xyz").expect("valid URL"),
            reactions: None,
            actions: Some(NoteActions {
                target: ActionTarget::Local {
                    user: "bob".to_string(),
                    uuid: "xyz".to_string(),
                },
                reacted: false,
                boosted: false,
                is_author: false,
            }),
        };

        let html = note.render().into_string();
        // Should contain both React and Boost buttons
        assert!(html.contains("❤️ React"));
        assert!(html.contains("🔁 Boost"));
        assert!(html.contains("/~bob/notes/xyz/react"));
        assert!(html.contains("/~bob/notes/xyz/announce"));
    }

    #[test]
    fn test_note_with_remote_actions() {
        let note = Note {
            author: NoteAuthor {
                display_name: "Remote User".to_string(),
                handle: NoteAuthorHandle::Remote {
                    username: "remote".to_string(),
                    host: "example.org".to_string(),
                    actor_url: "http://example.org/remote".to_string(),
                },
                icon_url: None,
            },
            content_html: "<p>Remote note</p>".to_string(),
            created_at: components::DateTime::from(&Utc::now()),
            updated_at: None,
            permalink: Url::try_from("http://example.org/notes/123").expect("valid URL"),
            reactions: None,
            actions: Some(NoteActions {
                target: ActionTarget::Remote { id: 42 },
                reacted: true,
                boosted: false,
                is_author: false,
            }),
        };

        let html = note.render().into_string();
        // Should show Unreact (already reacted) and Boost
        assert!(html.contains("❤️ Unreact"));
        assert!(html.contains("🔁 Boost"));
        assert!(html.contains("/remote-notes/42/unreact"));
        assert!(html.contains("/remote-notes/42/announce"));
    }

    #[test]
    fn test_note_content_html_is_not_escaped() {
        let note = Note {
            author: NoteAuthor {
                display_name: "Test".to_string(),
                handle: NoteAuthorHandle::Local {
                    username: "test".to_string(),
                    actor_url: "/~test".to_string(),
                },
                icon_url: None,
            },
            content_html: "<strong>Bold</strong> & <em>italic</em>".to_string(),
            created_at: components::DateTime::from(&Utc::now()),
            updated_at: None,
            permalink: Url::try_from("http://localhost/notes/1").expect("valid URL"),
            reactions: None,
            actions: None,
        };

        let html = note.render().into_string();
        // HTML should be rendered as-is, not escaped
        assert!(html.contains("<strong>Bold</strong>"));
        assert!(html.contains("<em>italic</em>"));
        assert!(html.contains(" & "));
    }
}

/// View for displaying a single note as a full page.
pub struct NotePage {
    pub note: Note,
    pub title: String,
}

impl Render for NotePage {
    fn render(&self) -> Markup {
        html! {
            div class="note-page" {
                h1 { (self.title) }
                (&self.note)
            }
        }
    }
}
