require "phlex"
require "pressa/views/icons"

module Pressa
  module Views
    class Layout < Phlex::HTML
      attr_reader :site,
        :page_subtitle,
        :page_description,
        :page_type,
        :canonical_url,
        :page_scripts,
        :page_styles,
        :content

      def initialize(
        site:,
        canonical_url:, page_subtitle: nil,
        page_description: nil,
        page_type: "website",
        page_scripts: [],
        page_styles: [],
        content: nil
      )
        @site = site
        @page_subtitle = page_subtitle
        @page_description = page_description
        @page_type = page_type
        @canonical_url = canonical_url
        @page_scripts = page_scripts
        @page_styles = page_styles
        @content = content
      end

      def view_template
        doctype

        html(lang: "en") do
          comment { "meow" }

          head do
            meta(charset: "UTF-8")
            title { full_title }
            meta(name: "twitter:title", content: full_title)
            meta(property: "og:title", content: full_title)
            meta(name: "description", content: description)
            meta(name: "twitter:description", content: description)
            meta(property: "og:description", content: description)
            meta(property: "og:site_name", content: site.title)

            link(rel: "canonical", href: canonical_url)
            meta(name: "twitter:url", content: canonical_url)
            meta(property: "og:url", content: canonical_url)
            meta(property: "og:image", content: og_image_url) if og_image_url
            meta(property: "og:type", content: page_type)
            meta(property: "article:author", content: site.author)
            meta(name: "twitter:card", content: "summary")

            link(
              rel: "alternate",
              href: site.url_for("/feed.xml"),
              type: "application/rss+xml",
              title: site.title
            )
            link(
              rel: "alternate",
              href: site.url_for("/feed.json"),
              type: "application/json",
              title: site.title
            )

            meta(name: "fediverse:creator", content: site.fediverse_creator) if site.fediverse_creator
            link(rel: "author", type: "text/plain", href: site.url_for("/humans.txt"))
            link(rel: "icon", type: "image/png", href: site.url_for("/images/favicon-32x32.png"))
            link(rel: "shortcut icon", href: site.url_for("/images/favicon.icon"))
            link(rel: "apple-touch-icon", href: site.url_for("/images/apple-touch-icon.png"))
            link(rel: "mask-icon", color: "#aa0000", href: site.url_for("/images/safari-pinned-tab.svg"))
            link(rel: "manifest", href: site.url_for("/images/manifest.json"))
            meta(name: "msapplication-config", content: site.url_for("/images/browserconfig.xml"))
            meta(name: "theme-color", content: "#121212")
            meta(name: "viewport", content: "width=device-width, initial-scale=1.0, viewport-fit=cover")
            link(rel: "dns-prefetch", href: "https://gist.github.com")

            all_styles.each do |style|
              link(rel: "stylesheet", type: "text/css", href: style_href(style.href))
            end
          end

          body do
            render_header
            render(content) if content
            render_footer
            render_scripts
          end
        end
      end

      private

      def description
        page_description || site.description
      end

      def full_title
        return site.title unless page_subtitle

        "#{site.title}: #{page_subtitle}"
      end

      def og_image_url
        site.image_url
      end

      def all_styles
        site.styles + page_styles
      end

      def all_scripts
        site.scripts + page_scripts
      end

      def render_header
        header(class: "primary") do
          div(class: "title") do
            h1 do
              a(href: site.url) { site.title }
            end

            h4 do
              plain "By "
              a(href: site.url_for("/about")) { site.author }
            end
          end

          nav(class: "remote") do
            ul do
              remote_nav_links.each do |link|
                li(class: remote_link_class(link)) do
                  attrs = {"aria-label": link.label, href: remote_link_href(link.href)}
                  attrs[:rel] = "me" if mastodon_link?(link)

                  a(**attrs) do
                    icon_markup = remote_link_icon_markup(link)
                    if icon_markup
                      raw(safe(icon_markup))
                    else
                      plain link.label
                    end
                  end
                end
              end
            end
          end

          nav(class: "local") do
            ul do
              li { a(href: site.url_for("/about")) { "About" } }
              li { a(href: site.url_for("/posts")) { "Archive" } }
              li { a(href: site.url_for("/projects")) { "Projects" } }
            end
          end

          div(class: "clearfix")
        end
      end

      def render_footer
        footer do
          plain "© #{footer_years} "
          a(href: site.url_for("/about")) { site.author }
        end
      end

      def render_scripts
        all_scripts.each do |scr|
          attrs = {src: script_src(scr.src)}
          attrs[:defer] = true if scr.defer
          script(**attrs)
        end

        render_gemini_fallback_script
      end

      def render_gemini_fallback_script
        # Inline so the behavior ships with the base HTML layout without needing
        # separate asset management for one small handler.
        script do
          raw(safe(<<~JS))
            (function () {
              function isPlainLeftClick(e) {
                return (
                  e.button === 0 &&
                  !e.defaultPrevented &&
                  !e.metaKey &&
                  !e.ctrlKey &&
                  !e.shiftKey &&
                  !e.altKey
                );
              }

              function setupGeminiFallback() {
                var links = document.querySelectorAll(
                  'header.primary nav.remote a[href^="gemini://"]'
                );
                if (!links || links.length === 0) return;

                for (var i = 0; i < links.length; i++) {
                  (function (link) {
                    link.addEventListener("click", function (e) {
                      if (!isPlainLeftClick(e)) return;

                      e.preventDefault();

                      var geminiHref = link.getAttribute("href");
                      var fallbackHref = "https://geminiprotocol.net";

                      var done = false;
                      var fallbackTimer = null;

                      function cleanup() {
                        if (fallbackTimer) window.clearTimeout(fallbackTimer);
                        document.removeEventListener("visibilitychange", onVisibilityChange);
                        window.removeEventListener("pagehide", onPageHide);
                        window.removeEventListener("blur", onBlur);
                      }

                      function markDone() {
                        done = true;
                        cleanup();
                      }

                      function onVisibilityChange() {
                        // If a handler opens and the browser backgrounded, consider it "successful".
                        if (document.visibilityState === "hidden") markDone();
                      }

                      function onPageHide() {
                        markDone();
                      }

                      function onBlur() {
                        // Some browsers blur the page when a protocol handler is invoked.
                        markDone();
                      }

                      document.addEventListener("visibilitychange", onVisibilityChange);
                      window.addEventListener("pagehide", onPageHide, { once: true });
                      window.addEventListener("blur", onBlur, { once: true });

                      // If we're still here shortly after attempting navigation, assume it failed.
                      fallbackTimer = window.setTimeout(function () {
                        if (done) return;
                        window.location.href = fallbackHref;
                      }, 900);

                      window.location.href = geminiHref;
                    });
                  })(links[i]);
                }
              }

              if (document.readyState === "loading") {
                document.addEventListener("DOMContentLoaded", setupGeminiFallback);
              } else {
                setupGeminiFallback();
              }
            })();
          JS
        end
      end

      def script_src(src)
        return src if src.start_with?("http://", "https://")

        absolute_asset(src)
      end

      def style_href(href)
        return href if href.start_with?("http://", "https://")

        absolute_asset(href)
      end

      def absolute_asset(path)
        normalized = path.start_with?("/") ? path : "/#{path}"
        site.url_for(normalized)
      end

      def footer_years
        current_year = Time.now.year
        start_year = site.copyright_start_year || current_year
        return current_year.to_s if start_year >= current_year

        "#{start_year} - #{current_year}"
      end

      def html_remote_links
        site.html_output_options&.remote_links || []
      end

      def remote_nav_links
        html_remote_links
      end

      def remote_link_href(href)
        return href if href.match?(/\A[a-z][a-z0-9+\-.]*:/i)

        absolute_asset(href)
      end

      def remote_link_class(link)
        slug = link.icon || link.label.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/^-|-$/, "")
        "remote-link #{slug}"
      end

      def remote_link_icon_markup(link)
        # Gemini doesn't have an obvious, widely-recognized protocol icon.
        # Use a simple custom SVG mark so it aligns like the other SVG icons.
        if link.icon == "gemini"
          return <<~SVG.strip
            <svg class="icon icon-gemini-protocol" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
              <path transform="translate(12 12) scale(0.84 1.04) translate(-12 -12)" d="M18,5.3C19.35,4.97 20.66,4.54 21.94,4L21.18,2.14C18.27,3.36 15.15,4 12,4C8.85,4 5.73,3.38 2.82,2.17L2.06,4C3.34,4.54 4.65,4.97 6,5.3V18.7C4.65,19.03 3.34,19.46 2.06,20L2.82,21.86C8.7,19.42 15.3,19.42 21.18,21.86L21.94,20C20.66,19.46 19.35,19.03 18,18.7V5.3M8,18.3V5.69C9.32,5.89 10.66,6 12,6C13.34,6 14.68,5.89 16,5.69V18.31C13.35,17.9 10.65,17.9 8,18.31V18.3Z"/>
            </svg>
          SVG
        end

        icon_renderer = remote_link_icon_renderer(link.icon)
        return nil unless icon_renderer

        Icons.public_send(icon_renderer)
      end

      def remote_link_icon_renderer(icon)
        case icon
        when "mastodon" then :mastodon
        when "github" then :github
        when "rss" then :rss
        when "code" then :code
        end
      end

      def mastodon_link?(link)
        link.icon == "mastodon"
      end
    end
  end
end
