use v6.e.PREVIEW;

use HTML::Functional;
use DB::BlogMeta;
use DB::Post;

unit class Config;

method generate-head($title, BlogMeta:D $meta, $description?) {
    head [
        meta :charset<utf-8>;
        meta :name<viewport>, :content<width=device-width, initial-scale=1>;
        meta :author :content<Nathan McCarty>;
        do if $title ~~ Str:D {
            title "$title — {$meta.title}";
        } else {
            title $meta.title;
        }
        # Add description, if one exists
        do if $description ~~ Str:D {
            meta :description :content($description)
        } else {
            []
        }
        # Preconnect to all our resource sources
        link :rel<preconnect> :href<https://static.stranger.systems>;
        link :rel<preconnect> :href<https://fonts.googleapis.com>;
        link :rel<preconnect> :href<https://fonts.gstatic.com> :crossorigin;
        link :rel<preconnect> :href<https://unpkg.com>;
        # Load fonts, Iosevka for code, Open Sans for content, and boxicons for
        # icons
        link :rel<stylesheet>,
            :href<https://static.stranger.systems/fonts/Iosevka/Iosevka.css>;
        link :rel<stylesheet>,
            :href<https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap>;
        link :rel<stylesheet>,
            :href<https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css>;
        # Link our style sheets
        link :rel<stylesheet>,
            :href</resources/colors.css>;
        link :rel<stylesheet>,
            :href</resources/main.css>;
        link :rel<stylesheet>,
            :href</resources/code.css>;
    ];
}

method site-header(BlogMeta:D $meta) {
    header :class<site-header>, [
        div :class<site-logo>, [
           # TODO: Use a real image here
           $meta.title
        ];
        div :class<site-tagline>, [
           $meta.tagline
        ];
        div :class<header-links>, [
            a :href</index.html>, [
                icon 'home';
                ' ';
                span [
                    'Home';
                ];
            ];
            a :href</archive.html>, [
                icon 'archive';
                ' ';
                span [
                    'Archive';
                ];
            ];
            a :href</tags.html>, [
                icon 'purchase-tag-alt';
                ' ';
                span [
                    'Tags';
                ];
            ];
            a :href</about.html>, [
                icon 'info-circle';
                ' ';
                span [
                    'About';
                ];
            ];
            a :href</atom.xml>, [
                icon 'rss';
                ' ';
                span [
                    'Feed';
                ];
            ];
        ];
    ]
}

method post-date(Post:D $post) {
    my $datetime = $post.posted-at;
    my $timestamp = sprintf(
        "%s %02d:%02d%s",
        $datetime.yyyy-mm-dd,
        ($datetime.hour % 12) || 12,
        $datetime.minute,
        $datetime.hour < 12 ?? 'am' !! 'pm'
    );

    div :class<post-time>, :title("Posted At $timestamp"), [
        icon 'time';
        '&nbsp;';
        $timestamp
    ]
}

method post-edit(Post:D $post) {
    return [] unless $post.edited-at.elems;
    my $datetime = $post.edited-at.max;
    my $timestamp = sprintf(
        "%s %02d:%02d%s",
        $datetime.yyyy-mm-dd,
        ($datetime.hour % 12) || 12,
        $datetime.minute,
        $datetime.hour < 12 ?? 'am' !! 'pm'
    );

    div :class<post-edit>, :title("Laste Edited At $timestamp"), [
        icon 'edit';
        '&nbsp;';
        $timestamp
    ]
}

sub mins-to-string($mins) {
    if $mins < 60 {
        $mins.Str ~ "m"
    } else {
        my $h = $mins div 60;
        my $m = $mins mod 60;
        $h.Str ~ "h" ~ $m.Str ~ "m"
    }
}

method post-read-time(Post:D $post) {
    my ($slow, $average, $fast) = $post.readtimes;
    div :class<post-read-time>, :title<Estimated read time at 140/180/220 WPM>, [
        icon 'timer';
        '&nbsp;';
        mins-to-string $slow;
        '&nbsp;';
        '/';
        '&nbsp;';
        mins-to-string $average;
        '&nbsp;';
        '/';
        '&nbsp;';
        mins-to-string $fast;
    ]
}

method post-tag(Str:D $tag) {
    span :class<post-tag>, [
        a :href("/tags/$tag.html"), [
            icon 'hash';
            $tag;
        ]
    ]
}

method post-tags(Post:D $post){
    sub intersperse (\element, +list) {
        gather for list {
            FIRST .take, next;
            take slip element, $_;
        }
    }
    my @tags = $post.tags;
    if @tags {
       @tags.=map(-> $tag {self.post-tag($tag)});
       div :class<post-tags>, [
           icon 'purchase-tag-alt';
           '&nbsp;';
           intersperse(', ', @tags);
       ]
    } else {
        []
    }
}

method post-info(Post:D $post) {
    div :class<post-info>, [
        self.post-date: $post;
        self.post-edit: $post;
        self.post-read-time: $post;
        self.post-tags: $post;
    ];
}

method post-header(Post:D $post) {
    header :class<post-header>, [
        div :class<post-title>, [
            h1 $post.title;
        ];
        self.post-info: $post;
    ]
}

# TODO: Support GFM admonitions
method generate-post(Post:D $post, BlogMeta:D $meta) {
    my $content = $post.render-html;
    my $head = self.generate-head($post.title, $meta, $post.description);
    my $body =
    body [
       self.site-header: $meta;
       article :class<post>, [
          self.post-header: $post;
          div :class<post-body>, [
              $content;
          ]
       ]
    ];
    # TODO: Setup footer
    # my $footer;

   my $html = html :lang<en>, [
       $head,
       $body
   ];

   "<!doctype html>$html"
}

method generate-blurb(Int:D $id, $db) {
    my $post = $db.posts{$id};
    my $desc = $post.description;
    my @slugs = $post.all-slugs;
    # Use the primary slug if there is one, the id if there isn't
    my $link = do if @slugs.elems {
       "/posts/by-slug/{@slugs[*-1]}.html"
    } else {
       "/posts/by-id/$id.html"
    }
    div :class<post-blurb>, [
        div :class<post-blurb-title>, [
            a :href($link), span [
                h2 $post.title;
            ];
        ];
        self.post-info: $post;
        if $desc ~~ Str:D {
            div :class<post-blurb-description>, [
                p $post.description;
            ];
        } else {
            []
        }
    ]
}

method generate-index($db) {
    my @most-recent =
    $db.sorted-posts
       .head(10)
       .grep(!*.value.hidden)
       .map(-> $pair {
            self.generate-blurb: $pair.key, $db
        });

    my $head = self.generate-head(Nil, $db.meta);
    my $body = body [
        self.site-header: $db.meta;
        div :class<post-blurbs>, [
           h1 "Recent Posts"
        ], @most-recent;
    ];

    my $html =
        html :lang<en>, [
            $head,
            $body
        ];

    "<!doctype html>$html"
}

method generate-archive($db) {
    my @most-recent =
    $db.sorted-posts
       .grep(!*.value.hidden)
       .map(-> $pair {
            self.generate-blurb: $pair.key, $db
        });

    my $head = self.generate-head(Nil, $db.meta);
    my $body = body [
        self.site-header: $db.meta;
        div :class<post-blurbs>, [
           h1 "All Posts"
        ], @most-recent;
    ];

    my $html =
        html :lang<en>, [
            $head,
            $body
        ];

    "<!doctype html>$html"
}

method generate-tag-blurb($db, $tag, $limit?) {
    sub post-to-link($id, $post) {
        my $desc = $post.description;
        my @slugs = $post.all-slugs;
        # Use the primary slug if there is one, the id if there isn't
        my $link = do if @slugs.elems {
            "/posts/by-slug/{@slugs[*-1]}.html"
        } else {
            "/posts/by-id/$id.html"
        }
        div :class<tag-blurb-post>, [
            div :class<tag-blurb-post-title>, [
                a :href($link), span [
                    h3 $post.title;
                ];
                self.post-info: $post;
                if $desc ~~ Str:D {
                    div :class<tag-blurb-post-description>, [
                        p $post.description;
                    ];
                } else {
                    []
                }
            ];
        ]
    }
    my @posts = $db.sorted-posts.grep(-> $a { $tag (elem) $a.value.tags });
    if $limit {
        @posts.=head($limit);
    }
    if @posts {
        div :class<tag-blurb>, [
            span :class<tag-blurb-title>, [
                a :href("/tags/$tag.html"), [
                    icon 'hash';
                    $tag;
                ];
            ];
            div :class<tag-blurb-links>,
                @posts.map(-> $a {post-to-link $a.key, $a.value});
        ]
    } else {
        []
    }
}

method generate-tags-page($db, @tags) {
    my $head = self.generate-head(Nil, $db.meta);
    my $body = body [
        self.site-header: $db.meta;
        div :class<tags>, [
            h1 "Tags";
        ], @tags.map(-> $tag {self.generate-tag-blurb($db, $tag, 4)});
    ];

    my $html =
        html :lang<en>, [
            $head,
            $body
        ];

    "<!doctype html>$html"
}

method generate-tag-page($db, $tag) {
    my $head = self.generate-head(Nil, $db.meta);
    my $body = body [
        self.site-header: $db.meta;
        self.generate-tag-blurb($db, $tag, 4);
    ];

    my $html =
        html :lang<en>, [
            $head,
            $body
        ];

    "<!doctype html>$html"
}

sub icon($icon) {
    i(:class("bx bx-$icon"))
}