require "json"
require "fileutils"

module Backlog
  # The global project index at ~/.cc-os/projects.json: a derived map of
  # canonical repo path -> {name, tracker, remote, updated_at}, maintained as
  # a side effect of config-write (issue #27). Deliberately GLOBAL state
  # (distinct from per-project .cc-os/ under ADR-0027) so any session can ask
  # "which tracker does project X use" without visiting its repo.
  #
  # Pure over an injected path: callers hand in the already-canonical repo
  # path (the CLI resolves it via `git rev-parse --show-toplevel`). Read
  # paths never raise — a missing or corrupt file reads as an empty index; a
  # corrupt file is only overwritten when a new upsert happens. Writes are
  # atomic (temp file + rename in the same directory).
  class ProjectIndex
    VERSION = 1

    # Default index path, honoring a CC_OS_HOME override (used by tests to
    # keep the real index untouched) and falling back to $HOME/.cc-os.
    def self.default_path(env: ENV)
      base = env["CC_OS_HOME"] || File.join(env.fetch("HOME", Dir.home), ".cc-os")
      File.join(base, "projects.json")
    end

    def initialize(path: self.class.default_path, today: -> { Time.now.strftime("%Y-%m-%d") })
      @path = path
      @today = today
    end

    # @return [Hash] the projects map (canonical path -> row); {} when the
    #   file is missing or unreadable/corrupt. Never raises.
    def all
      data = read
      data["projects"].is_a?(Hash) ? data["projects"] : {}
    end

    # Insert or update the row for a canonical repo path. Atomic write;
    # creates the parent directory if missing.
    def upsert(path:, name:, tracker:, remote:)
      projects = all
      projects[path] = {
        "name" => name,
        "tracker" => tracker,
        "remote" => remote,
        "updated_at" => @today.call
      }
      write({ "version" => VERSION, "projects" => projects })
      projects[path]
    end

    private

    def read
      return {} unless File.exist?(@path)

      parsed = JSON.parse(File.read(@path))
      parsed.is_a?(Hash) ? parsed : {}
    rescue StandardError
      {}
    end

    def write(data)
      dir = File.dirname(@path)
      FileUtils.mkdir_p(dir)
      temp = File.join(dir, ".projects.json.tmp.#{Process.pid}")
      File.write(temp, JSON.pretty_generate(data) + "\n")
      File.rename(temp, @path)
    end
  end
end
