/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2026 metamuffin <metamuffin.org>
*/

use crate::{
    Page,
    filter_sort::NodeFilterSortForm,
    format::format_chapter,
    locale::{Language, trs},
    node_card::{NodeCard, NodeCardWide},
    props::Props,
};
use jellycommon::{
    Chapter, CreditCategory, IdentifierType, Node, NodeKind, PictureSlot,
    api::NodeFilterSort,
    routes::{
        u_node_image, u_node_slug, u_node_slug_person_asset, u_node_slug_player,
        u_node_slug_player_time, u_node_slug_thumbnail, u_node_slug_update_rating,
        u_node_slug_watched,
    },
    user::{ApiWatchedState, NodeUserData, WatchedState},
};
use std::sync::Arc;

impl Page for NodePage<'_> {
    fn title(&self) -> String {
        self.node.title.clone().unwrap_or_default()
    }
    fn class(&self) -> Option<&'static str> {
        if self.player {
            Some("player")
        } else {
            Some("node-page")
        }
    }
    fn to_render(&self) -> markup::DynRender<'_> {
        markup::new!(@self)
    }
}

markup::define! {
    NodePage<'a>(
        node: &'a Node,
        udata: &'a NodeUserData,
        children: &'a [(Arc<Node>, NodeUserData)],
        parents: &'a [(Arc<Node>, NodeUserData)],
        similar: &'a [(Arc<Node>, NodeUserData)],
        filter: &'a NodeFilterSort,
        lang: &'a Language,
        player: bool,
    ) {
        @if !matches!(node.kind, NodeKind::Collection) && !player {
            img.backdrop[src=u_node_image(&node.slug, PictureSlot::Backdrop, 2048), loading="lazy"];
        }
        .page.node {
            @if !matches!(node.kind, NodeKind::Collection) && !player {
                @let cls = format!("bigposter {}", aspect_class(node.kind));
                div[class=cls] { img[src=u_node_image(&node.slug, PictureSlot::Cover, 2048), loading="lazy"]; }
            }
            .title {
                h1 { @node.title }
                ul.parents { @for (node, _) in *parents { li {
                    a.component[href=u_node_slug(&node.slug)] { @node.title }
                }}}
                @if node.media.is_some() {
                    a.play[href=u_node_slug_player(&node.slug)] { @trs(lang, "node.player_link") }
                }
                @if !matches!(node.kind, NodeKind::Collection | NodeKind::Channel) {
                    @if matches!(udata.watched, WatchedState::None | WatchedState::Pending | WatchedState::Progress(_)) {
                        form.mark_watched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::Watched)] {
                            input[type="submit", value=trs(lang, "node.watched.set")];
                        }
                    }
                    @if matches!(udata.watched, WatchedState::Watched) {
                        form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::None)] {
                            input[type="submit", value=trs(lang, "node.watched.unset")];
                        }
                    }
                    @if matches!(udata.watched, WatchedState::None) {
                        form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::Pending)] {
                            input[type="submit", value=trs(lang, "node.watchlist.set")];
                        }
                    }
                    @if matches!(udata.watched, WatchedState::Pending) {
                        form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::None)] {
                            input[type="submit", value=trs(lang, "node.watchlist.unset")];
                        }
                    }
                    form.rating[method="POST", action=u_node_slug_update_rating(&node.slug)] {
                        input[type="range", name="rating", min=-10, max=10, step=1, value=udata.rating];
                        input[type="submit", value=trs(lang, "node.update_rating")];
                    }
                }
            }
            .details {
                @Props { node, udata, full: true, lang }
                h3 { @node.tagline }
                @if let Some(description) = &node.description {
                    p { @for line in description.lines() { @line br; } }
                }
                @if let Some(media) = &node.media {
                    @if !media.chapters.is_empty() {
                        h2 { @trs(lang, "node.chapters") }
                        ul.children.hlist { @for chap in &media.chapters {
                            @let (inl, sub) = format_chapter(chap);
                            li { .card."aspect-thumb" {
                                .poster {
                                    a[href=u_node_slug_player_time(&node.slug, chap.time_start.unwrap_or(0.))] {
                                        img[src=u_node_slug_thumbnail(&node.slug, chapter_key_time(chap, media.duration), 1024), loading="lazy"];
                                    }
                                    .cardhover { .props { p { @inl } } }
                                }
                                .title { span { @sub } }
                            }}
                        }}
                    }
                    @if !node.credits.is_empty() {
                        h2 { @trs(lang, "node.people") }
                        @for (group, people) in &node.credits {
                            details[open=group==&CreditCategory::Cast] {
                                summary { h3 { @format!("{}", group) } }
                                ul.children.hlist { @for (i, pe) in people.iter().enumerate() {
                                    li { .card."aspect-port" {
                                        .poster {
                                            a[href="#"] {
                                                img[src=u_node_slug_person_asset(&node.slug, *group, i, 1024), loading="lazy"];
                                            }
                                        }
                                        .title {
                                            // TODO span { @pe.person.name } br;
                                            @if let Some(c) = pe.characters.first() {
                                                span.subtitle { @c }
                                            }
                                            @if let Some(c) = pe.jobs.first() {
                                                span.subtitle { @c }
                                            }
                                        }
                                    }}
                                }}
                            }
                        }
                    }
                    details {
                        summary { @trs(lang, "media.tracks") }
                        ol { @for track in &media.tracks {
                            li { @format!("{track}") }
                        }}
                    }
                }
                @if !node.identifiers.is_empty() {
                    details {
                        summary { @trs(lang, "node.external_ids") }
                        table {
                            @for (key, value) in &node.identifiers { tr {
                                tr {
                                    td { @trs(lang, &format!("id.{}", key)) }
                                    @if let Some(url) = external_id_url(*key, value) {
                                        td { a[href=url] { pre { @value } } }
                                    } else {
                                        td { pre { @value } }
                                    }
                                }
                            }}
                        }
                    }
                }
                @if !node.tags.is_empty() {
                    details {
                        summary { @trs(lang, "node.tags") }
                        ol { @for tag in &node.tags {
                            li { @tag }
                        }}
                    }
                }
            }
            @if matches!(node.kind, NodeKind::Collection | NodeKind::Channel) {
                @NodeFilterSortForm { f: filter, lang }
            }
            @if !similar.is_empty() {
                h2 { @trs(lang, "node.similar") }
                ul.children.hlist {@for (node, udata) in similar.iter() {
                    li { @NodeCard { node, udata, lang } }
                }}
            }
            @match node.kind {
                NodeKind::Show | NodeKind::Series | NodeKind::Season => {
                    ol { @for (node, udata) in children.iter() {
                        li { @NodeCardWide { node, udata, lang } }
                    }}
                }
                NodeKind::Collection | NodeKind::Channel | _ => {
                    ul.children {@for (node, udata) in children.iter() {
                        li { @NodeCard { node, udata, lang } }
                    }}
                }
            }
        }
    }
}

fn chapter_key_time(c: &Chapter, dur: f64) -> f64 {
    let start = c.time_start.unwrap_or(0.);
    let end = c.time_end.unwrap_or(dur);
    start * 0.8 + end * 0.2
}

pub fn aspect_class(kind: NodeKind) -> &'static str {
    use NodeKind::*;
    match kind {
        Video | Episode => "aspect-thumb",
        Collection => "aspect-land",
        Season | Show | Series | Movie | ShortFormVideo => "aspect-port",
        Channel | Music | Unknown => "aspect-square",
    }
}

fn external_id_url(key: IdentifierType, value: &str) -> Option<String> {
    Some(match key {
        IdentifierType::YoutubeVideo => format!("https://youtube.com/watch?v={value}"),
        IdentifierType::YoutubeChannel => format!("https://youtube.com/channel/{value}"),
        IdentifierType::YoutubeChannelHandle => format!("https://youtube.com/channel/@{value}"),
        IdentifierType::MusicbrainzRelease => format!("https://musicbrainz.org/release/{value}"),
        IdentifierType::MusicbrainzArtist => format!("https://musicbrainz.org/artist/{value}"),
        IdentifierType::MusicbrainzReleaseGroup => {
            format!("https://musicbrainz.org/release-group/{value}")
        }
        IdentifierType::MusicbrainzRecording => {
            format!("https://musicbrainz.org/recording/{value}")
        }
        _ => return None,
    })
}
