require "yaml"
require "date"

module Adr
  # One ADR file: frontmatter + titled body sections. Parses and serializes;
  # knows nothing about the filesystem.
  class Record
    ParseError = Class.new(StandardError)

    FRONTMATTER_ORDER = %w[
      id date status supersedes superseded-by
      affected-paths affected-components migration_confidence
    ].freeze
    QUOTED_FIELDS = %w[id supersedes superseded-by].freeze
    SECTION_ORDER = ["Context", "Decision", "Consequences", "Alternatives rejected"].freeze

    attr_reader :frontmatter, :title, :sections

    def self.parse(text)
      match = text.match(/\A---\n(.*?)\n---\n(.*)\z/m)
      raise ParseError, "no frontmatter block found" unless match

      frontmatter = YAML.safe_load(match[1], permitted_classes: [Date]) || {}
      body = match[2]
      heading = body[/^# +(.+)$/, 1].to_s.strip
      title = heading.sub(/\A\S+\s+—\s+/, "")
      sections = {}
      body.scan(/^## ([^\n]+)\n(.*?)(?=^## |\z)/m) do |name, content|
        sections[name.strip] = content.strip
      end
      new(frontmatter: frontmatter, title: title, sections: sections)
    end

    def initialize(frontmatter:, title:, sections:)
      @frontmatter = frontmatter
      @title = title
      @sections = sections
    end

    def id      = frontmatter["id"]&.to_s
    def status  = frontmatter["status"]
    def date    = frontmatter["date"]

    def supersede_with(new_id)
      frontmatter["superseded-by"] = new_id
      frontmatter["status"] = "Superseded"
    end

    def to_markdown
      lines = ["---"]
      FRONTMATTER_ORDER.each do |field|
        next unless frontmatter.key?(field)
        lines << "#{field}: #{format_value(field, frontmatter[field])}".rstrip
      end
      (frontmatter.keys - FRONTMATTER_ORDER).each do |field|
        lines << "#{field}: #{format_value(field, frontmatter[field])}".rstrip
      end
      lines << "---" << "" << "# #{id} — #{title}"
      sections.each do |name, content|
        lines << "" << "## #{name}" << "" << content
      end
      lines.join("\n") + "\n"
    end

    private

    def format_value(field, value)
      case value
      when nil   then ""
      when Array then "[#{value.join(', ')}]"
      when Date  then value.iso8601
      else
        text = value.to_s
        if QUOTED_FIELDS.include?(field) || text.match?(/[:#]/)
          text.inspect
        else
          text
        end
      end
    end
  end
end
