# typed: strict
# frozen_string_literal: true

require "requirement"

# An adapter for casks to provide dependency information in a formula-like interface.
class CaskDependent
  # Defines a dependency on another cask
  class Requirement < ::Requirement
    Cache = type_template { { fixed: T::Hash[String, T.untyped] } }

    satisfy(build_env: false) do
      cask_token = cask
      raise "unexpected nil cask" unless cask_token

      Cask::CaskLoader.load(cask_token).installed?
    end
  end

  sig { returns(Cask::Cask) }
  attr_reader :cask

  sig { params(cask: Cask::Cask).void }
  def initialize(cask)
    @cask = cask
  end

  sig { returns(String) }
  def name
    @cask.token
  end

  sig { returns(String) }
  def full_name
    @cask.full_name
  end

  sig { params(read_from_tab: T::Boolean, undeclared: T::Boolean).returns(T::Array[Dependency]) }
  def runtime_dependencies(read_from_tab: true, undeclared: true)
    deps.flat_map do |dep|
      [dep, *dep.to_installed_formula.runtime_dependencies(read_from_tab:, undeclared:)]
    end.uniq
  end

  sig { returns(T::Array[Dependency]) }
  def deps
    @deps ||= T.let(
      @cask.depends_on.formula.map do |f|
        Dependency.new f
      end,
      T.nilable(T::Array[Dependency]),
    )
  end

  sig { returns(T::Array[::Requirement]) }
  def requirements
    @requirements ||= T.let(
      begin
        requirements = []
        dsl_reqs = @cask.depends_on

        dsl_reqs.arch&.each do |arch|
          arch = if arch[:bits] == 64
            if arch[:type] == :intel
              :x86_64
            else
              :"#{arch[:type]}64"
            end
          elsif arch[:type] == :intel && arch[:bits] == 32
            :i386
          else
            arch[:type]
          end
          requirements << ArchRequirement.new([arch])
        end
        dsl_reqs.cask.each do |cask_ref|
          requirements << CaskDependent::Requirement.new([{ cask: cask_ref }])
        end
        requirements << dsl_reqs.linux if dsl_reqs.linux
        requirements << dsl_reqs.macos if dsl_reqs.macos
        requirements << dsl_reqs.maximum_macos if dsl_reqs.maximum_macos

        requirements
      end,
      T.nilable(T::Array[::Requirement]),
    )
  end

  sig {
    params(
      block: T.nilable(T.proc.params(arg0: T.any(Formula, CaskDependent, SoftwareSpec),
                                     arg1: ::Dependency).returns(T.nilable(Symbol))),
    ).returns(T::Array[::Dependency])
  }
  def recursive_dependencies(&block)
    Dependency.expand(self, &block)
  end

  sig {
    params(
      block: T.nilable(T.proc.params(arg0: T.any(Formula, CaskDependent, SoftwareSpec),
                                     arg1: ::Requirement).returns(T.nilable(Symbol))),
    ).returns(Requirements)
  }
  def recursive_requirements(&block)
    Requirement.expand(self, &block)
  end

  sig { returns(T::Boolean) }
  def any_version_installed?
    @cask.installed?
  end
end
