#!/usr/bin/env ruby

require 'optionparser'

class DistroMap
  attr_reader :entries

  def initialize(map = nil)
    @entries = map || self.class.builtin_map
  end
  # Returns the map for our distros.
  #
  # The key in each case is a string containing a lowercase OS name, a slash,
  # and a version number.  The value is a map containing the following fields:
  #
  # name:: a human-readable name for this distro.
  # component:: a component suitable for a packagecloud.io URL.
  # image:: a Docker image name from build_dockers without any extension.
  # equivalent:: packagecloud.io components for which we can upload the same
  # package.
  # package_type:: the extension for the package format on this OS.
  # package_tag:: the trailing component after the version number on this OS.
  def self.builtin_map
    {
      # RHEL EOL https://access.redhat.com/support/policy/updates/errata
      # Fedora EOL https://docs.fedoraproject.org/en-US/releases/
      # SLES EOL https://www.suse.com/lifecycle/
      # opensuse https://en.opensuse.org/Lifetime
      # or https://en.wikipedia.org/wiki/OpenSUSE_version_history
      "centos/8" => {
        name: "RPM RHEL 8/Rocky Linux 8",
        component: "el/8",
        image: "centos_8",
        package_type: "rpm",
        package_tag: "-1.el8",
        equivalent: [
          "el/8",                      # EOL May 2029
        ],
      },
      "rocky/9" => {
        name: "RPM RHEL 9/Rocky Linux 9",
        component: "el/9",
        image: "rocky_9",
        package_type: "rpm",
        package_tag: "-1.el9",
        equivalent: [
          "el/9",                      # EOL May 2032
          "sles/15.7",                 # EOL July 2031
        ],
      },
      "rocky/10" => {
        name: "RPM RHEL 10/Rocky Linux 10",
        component: "el/10",
        image: "rocky_10",
        package_type: "rpm",
        package_tag: "-1.el10",
        equivalent: [
          "el/10",                     # EOL May 2035
          "fedora/43",                 # EOL December 2026
          "fedora/44",                 # EOL May 2027
          "opensuse/16.0",             # EOL October 2027
          "sles/16.0",                 # EOL November 2027
        ],
      },
      # Debian EOL https://wiki.debian.org/LTS/
      # Ubuntu EOL https://wiki.ubuntu.com/Releases
      # Mint EOL https://linuxmint.com/download_all.php
      "debian/11" => {
        name: "Debian 11",
        component: "debian/bullseye",
        image: "debian_11",
        package_type: "deb",
        package_tag: "",
        equivalent: [
          "debian/bullseye",           # EOL August 2026
          "linuxmint/vanessa",         # EOL April 2027
          "linuxmint/vera",            # EOL April 2027
          "linuxmint/victoria",        # EOL April 2027
          "linuxmint/virginia",        # EOL April 2027
          "ubuntu/jammy",              # EOL April 2027
        ],
      },
      "debian/12" => {
        name: "Debian 12",
        component: "debian/bookworm",
        image: "debian_12",
        package_type: "deb",
        package_tag: "",
        equivalent: [
          "debian/bookworm",           # EOL June 2028
          "linuxmint/faye",            # LMDE LTS release based on Debian 12
          "linuxmint/wilma",           # EOL April 2029
          "linuxmint/xia",             # EOL April 2029
          "linuxmint/zara",            # EOL April 2029
          "linuxmint/zena",            # EOL April 2029
          "ubuntu/noble",              # EOL June 2029
        ]
      },
      "debian/13" => {
        name: "Debian 13",
        component: "debian/trixie",
        image: "debian_13",
        package_type: "deb",
        package_tag: "",
        equivalent: [
          "debian/trixie",             # EOL June 2030
          "debian/forky",              # Current testing (Debian 14)
          "linuxmint/gigi",            # LMDE LTS release based on Debian 13
          "ubuntu/resolute",           # EOL July 2031
        ]
      },
    }
  end

  def distro_name_map
    entries.map { |k, v| [k, v[:equivalent]] }.to_h
  end

  def image_names
    entries.values.map { |v| v[:image] }.to_a
  end
end

class DistroMapProgram
  def initialize(stdout, stderr, dmap = nil)
    @dmap = DistroMap.new(dmap)
    @stdout = stdout
    @stderr = stderr
  end

  def image_names
    @stdout.puts @dmap.image_names.join(" ")
  end

  def distro_markdown
    platform_architectures = {
      "rpm" => [
        { arch: ".x86_64" },
      ],
      "deb" => [
        { arch: "_amd64", name: "AMD64" },
        { arch: "_arm64", name: "ARM64" },
      ],
    }
    separator = {
      "rpm" => "-",
      "deb" => "_",
    }
    result = @dmap.entries.values.map do |entry|
      type = entry[:package_type]
      platform_architectures[type].map do |arch|
        arch_name = arch.key?(:name) ? " (#{arch[:name]})" : ""
        "[#{entry[:name]}#{arch_name}](https://packagecloud.io/github/git-lfs/packages/#{entry[:component]}/git-lfs#{separator[type]}VERSION#{entry[:package_tag]}#{arch[:arch]}.#{type}/download)\n"
      end.join
    end.join
    @stdout.puts result
  end

  def run(args)
    options = {}
    OptionParser.new do |parser|
      parser.on("--image-names", "Print the names of all images") do
        options[:mode] = :image_names
      end

      parser.on("--distro-markdown", "Print links to packages for all distros") do
        options[:mode] = :distro_markdown
      end
    end.parse!(args)

    case options[:mode]
    when nil
      @stderr.puts "A mode option is required"
      2
    when :image_names
      image_names
      0
    when :distro_markdown
      distro_markdown
      0
    end
  end
end

if $PROGRAM_NAME == __FILE__
  exit DistroMapProgram.new($stdout, $stderr).run(ARGV)
end
