require "kramdown"
require "pressa/posts/models"
require "pressa/posts/metadata"

module Pressa
  module Posts
    class PostRepo
      EXCERPT_LENGTH = 300

      def initialize(output_path: "posts")
        @output_path = output_path
        @posts_by_year = {}
      end

      def read_posts(posts_dir)
        enumerate_markdown_files(posts_dir) do |file_path|
          post = read_post(file_path)
          add_post_to_hierarchy(post)
        end

        PostsByYear.new(by_year: @posts_by_year)
      end

      private

      def enumerate_markdown_files(dir, &block)
        Dir.glob(File.join(dir, "**", "*.md")).each(&block)
      end

      def read_post(file_path)
        content = File.read(file_path)
        metadata = PostMetadata.parse(content)

        body_markdown = content.sub(/\A---\s*\n.*?\n---\s*\n/m, "")

        html_body = render_markdown(body_markdown)

        slug = File.basename(file_path, ".md")
        path = generate_path(slug, metadata.date)
        excerpt = generate_excerpt(body_markdown)

        Post.new(
          slug:,
          title: metadata.title,
          author: metadata.author,
          date: metadata.date,
          formatted_date: metadata.formatted_date,
          link: metadata.link,
          tags: metadata.tags,
          body: html_body,
          excerpt:,
          path:
        )
      end

      def render_markdown(markdown)
        Kramdown::Document.new(
          markdown,
          input: "GFM",
          hard_wrap: false,
          syntax_highlighter: "rouge",
          syntax_highlighter_opts: {
            line_numbers: false,
            wrap: true
          }
        ).to_html
      end

      def generate_path(slug, date)
        year = date.year
        month = format("%02d", date.month)
        "/#{@output_path}/#{year}/#{month}/#{slug}"
      end

      def generate_excerpt(markdown)
        text = markdown.dup

        text.gsub!(/!\[[^\]]*\]\([^)]+\)/, "")
        text.gsub!(/!\[[^\]]*\]\[[^\]]+\]/, "")

        text.gsub!(/\[([^\]]+)\]\([^)]+\)/, '\1')
        text.gsub!(/\[([^\]]+)\]\[[^\]]+\]/, '\1')

        text.gsub!(/(?m)^\[[^\]]+\]:\s*\S.*$/, "")

        text.gsub!(/<[^>]+>/, "")

        text.gsub!(/\s+/, " ")
        text.strip!

        return "..." if text.empty?

        "#{text[0...EXCERPT_LENGTH]}..."
      end

      def add_post_to_hierarchy(post)
        year = post.year
        month_num = post.month

        @posts_by_year[year] ||= create_year_posts(year)
        year_posts = @posts_by_year[year]

        month_posts = year_posts.by_month[month_num]
        if month_posts
          updated_posts = month_posts.posts + [post]
          year_posts.by_month[month_num] = MonthPosts.new(
            month: month_posts.month,
            posts: updated_posts
          )
        else
          month = Month.from_date(post.date)
          year_posts.by_month[month_num] = MonthPosts.new(
            month:,
            posts: [post]
          )
        end
      end

      def create_year_posts(year)
        YearPosts.new(year:, by_month: {})
      end
    end
  end
end
