require "http/client"
require "json"
require "colorize"

module Lattice
  module CLI
    # Compares the running Lattice version against the latest release published
    # on Codeberg and reports whether an update is available.
    #
    # This mirrors `velvet check-update` adapted for Crystal / the Lattice repo.
    class CheckUpdate
      RELEASES_API_URL = "https://codeberg.org/api/v1/repos/DaZhi-the-Revelator/lattice/releases?limit=1"
      TAGS_API_URL     = "https://codeberg.org/api/v1/repos/DaZhi-the-Revelator/lattice/tags?limit=1"

      # A minimal struct for deserialising the Codeberg releases API response.
      private struct CodebergRelease
        include JSON::Serializable

        @[JSON::Field(key: "tag_name")]
        property tag_name : String
      end

      # A minimal struct for deserialising the Codeberg tags API response.
      private struct CodebergTag
        include JSON::Serializable

        @[JSON::Field(key: "name")]
        property name : String
      end

      # Compares two "X.Y.Z" version strings.
      # Returns  1 if *a* > *b*, -1 if *a* < *b*, 0 if equal.
      # Non-numeric components fall back to lexicographic comparison.
      def self.semver_cmp(a : String, b : String) : Int32
        a_parts = a.split('.')
        b_parts = b.split('.')
        max_len = {a_parts.size, b_parts.size}.max
        max_len.times do |i|
          av = i < a_parts.size ? (a_parts[i].to_i? || 0) : 0
          bv = i < b_parts.size ? (b_parts[i].to_i? || 0) : 0
          return 1  if av > bv
          return -1 if av < bv
        end
        0
      end

      def self.run : NoReturn
        local_version = Lattice::VERSION.strip

        puts "Checking for Lattice updates..."
        puts "Local version: #{local_version}"

        response = begin
          HTTP::Client.get(RELEASES_API_URL)
        rescue ex
          STDERR.puts "Failed to reach Codeberg API: #{ex.message}"
          exit 1
        end

        remote_version = if response.status_code == 200
          parse_remote_version_from_releases(response.body)
        else
          # Fall back to the tags endpoint (works even without a formal release).
          tags_response = begin
            HTTP::Client.get(TAGS_API_URL)
          rescue ex
            STDERR.puts "Failed to reach Codeberg API: #{ex.message}"
            exit 1
          end
          unless tags_response.status_code == 200
            STDERR.puts "Codeberg API returned status #{tags_response.status_code} (releases) and #{response.status_code} (tags)"
            exit 1
          end
          parse_remote_version_from_tags(tags_response.body)
        end

        puts "Latest release: #{remote_version}"

        cmp = semver_cmp(local_version, remote_version)
        case cmp
        when 0
          puts "Lattice is up to date (#{local_version}).".colorize(:green)
        when .>(0)
          puts "[AHEAD] Local build #{local_version} is newer than the latest release (#{remote_version}).".colorize(:cyan)
        else
          puts "[UPDATE] Lattice #{remote_version} is available (you have #{local_version}).".colorize(:yellow)
          puts ""
          puts "To update, run:"
          puts "  cd lattice && git pull && shards build --release"
          puts "  then copy bin/lattice to your PATH"
        end

        exit 0
      end

      private def self.parse_remote_version_from_releases(body : String) : String
        releases = begin
          Array(CodebergRelease).from_json(body)
        rescue ex
          STDERR.puts "Failed to parse releases response: #{ex.message}"
          exit 1
        end
        if releases.empty?
          STDERR.puts "No releases found on Codeberg."
          exit 1
        end
        ver = releases.first.tag_name.lstrip('v').strip
        if ver.empty?
          STDERR.puts "Could not determine latest release version."
          exit 1
        end
        ver
      end

      private def self.parse_remote_version_from_tags(body : String) : String
        tags = begin
          Array(CodebergTag).from_json(body)
        rescue ex
          STDERR.puts "Failed to parse tags response: #{ex.message}"
          exit 1
        end
        if tags.empty?
          STDERR.puts "No tags found on Codeberg."
          exit 1
        end
        ver = tags.first.name.lstrip('v').strip
        if ver.empty?
          STDERR.puts "Could not determine latest tag version."
          exit 1
        end
        ver
      end
    end
  end
end
