module Backlog
  # Validates and classifies tracker keys (the value written to
  # `.cc-os/config`'s `tracker` field) and parses `git remote -v` output to
  # detect a Forgejo/GitHub remote. Pure — no filesystem, no network.
  class Tracker
    DEFAULT_FORGEJO_HOST = "forgejo.swansoncloud.com"

    # ADR-0042 retired Planka: the tracker grammar is exactly these three
    # forms. `planka:` is deliberately absent — it is rejected, not merely
    # unmatched (see config-write and os-status's tracker-configured check).
    FORMATS = {
      forgejo: %r{\Aforgejo:[\w.-]+/[\w.-]+\z},
      github: %r{\Agithub:[\w.-]+/[\w.-]+\z},
      repo: /\Arepo:.+\z/
    }.freeze

    # @return [Boolean] true if value matches one of the three valid formats
    def self.valid?(value)
      !kind(value).nil?
    end

    # @return [Symbol, nil] :forgejo / :github / :repo, or nil if invalid
    def self.kind(value)
      return nil unless value.is_a?(String)

      FORMATS.each { |name, pattern| return name if value.match?(pattern) }
      nil
    end

    # Where /to-issues output (spec slices) should be published, given the
    # repo's tracker key. One tracker holds both state and specs (ADR-0042):
    # forgejo:/github: keys route spec slices to that git issue tracker;
    # repo:<path> routes to the in-repo issue-file convention; nil or any
    # invalid key (including retired `planka:` values) is :unrouted.
    #
    # @return [Symbol] :git_issues | :repo_files | :unrouted
    def self.issues_destination(value)
      case kind(value)
      when :forgejo, :github then :git_issues
      when :repo then :repo_files
      else :unrouted
      end
    end

    # Parse the first remote line of `git remote -v` output and classify it
    # as a Forgejo or GitHub remote (or :unknown for anything else).
    #
    # @param output [String] raw `git remote -v` output
    # @param forgejo_host [String] hostname that identifies the user's
    #   self-hosted Forgejo instance
    # @return [Hash, nil] {kind:, host:, owner:, repo:}, or nil if no remote
    #   URL could be parsed
    def self.parse_remote(output, forgejo_host: DEFAULT_FORGEJO_HOST)
      line = output.to_s.lines.first
      return nil unless line

      url = line.split(/\s+/)[1]
      return nil unless url

      host, path = split_host_path(url)
      return nil unless host && path

      owner, repo = path.sub(/\.git\z/, "").split("/").last(2)
      return nil unless owner && repo

      kind = if host == forgejo_host
               :forgejo
             elsif host == "github.com"
               :github
             else
               :unknown
             end

      { kind: kind, host: host, owner: owner, repo: repo }
    end

    def self.split_host_path(url)
      if url =~ %r{\A[\w+.-]+://(?:[^/@]+@)?([^:/]+)(?::\d+)?/(.+)\z}
        [Regexp.last_match(1), Regexp.last_match(2)]
      elsif url =~ /\A[^@\s]+@([^:]+):(.+)\z/
        [Regexp.last_match(1), Regexp.last_match(2)]
      end
    end
    private_class_method :split_host_path
  end
end
