# SPDX-FileCopyrightText: 2019 Greg Mefford
# SPDX-FileCopyrightText: 2020 Frank Hunleth
# SPDX-FileCopyrightText: 2020 Justin Schneck
# SPDX-FileCopyrightText: 2022 Jon Carstens
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule Mix.Nerves.Preflight do
  @moduledoc false
  alias Nerves.Utils.WSL

  @fwup_semver "~> 1.8"

  @doc """
  Run the preflight checks for the Nerves System
  """
  @spec check!() :: any()
  def check!() do
    :os.type()
    |> case do
      {_, :linux} -> if WSL.running_on_wsl?(), do: :wsl, else: :linux
      {_, type} -> type
    end
    |> check_platform!()

    Mix.Task.run("nerves.loadpaths")
  end

  # OSX
  defp check_platform!(:darwin) do
    ensure_fwup_version!()
    ensure_available!("mksquashfs", package: "squashfs")
    ensure_available!("gstat", package: "gstat (coreutils)")
  end

  # NOTE: We currently require fwup to be installed both in WSL and in Windows
  # because the fwup.exe in Windows is used when burning firmware to a physical
  # memory card, and fwup in WSL is used for most other operations.
  #
  # WSL 2 adds some new lower-level integrations that may eliminate the need
  # for this in the future, but we'll need to do some more research.
  defp check_platform!(:wsl) do
    ensure_fwup_version!()
    ensure_fwup_version!("fwup.exe")
    ensure_available!("mksquashfs", package: "squashfs")
  end

  # Non-WSL Linux
  defp check_platform!(_) do
    ensure_fwup_version!()
    ensure_available!("mksquashfs", package: "squashfs")
  end

  @doc """
  Ensure fwup executable is available and is the expected version
  """
  @spec ensure_fwup_version!(String.t(), String.t()) :: :ok
  def ensure_fwup_version!(fwup_bin \\ "fwup", vsn_requirement \\ @fwup_semver) do
    ensure_available!(fwup_bin)

    case Nerves.Port.cmd(fwup_bin, ["--version"]) do
      {vsn, 0} ->
        vsn = String.trim(vsn)
        {:ok, req} = Version.parse_requirement(vsn_requirement)

        if !Version.match?(vsn, req) do
          Mix.raise("""
          #{fwup_bin} #{vsn_requirement} is required for Nerves.

          You are running #{vsn}.
          Please see https://hexdocs.pm/nerves/installation.html#fwup
          for installation instructions
          """)
        end

      error ->
        Mix.raise("""
        Nerves encountered an error while checking host requirements for fwup
        #{inspect(error)}
        Please open a bug report for this issue on github.com/nerves-project/nerves
        """)
    end

    :ok
  end

  @doc """
  Ensure that the given command is available in the system path
  """
  @spec ensure_available!(String.t(), Keyword.t()) :: :ok
  def ensure_available!(executable, opts \\ []) do
    if System.find_executable(executable) do
      :ok
    else
      package = opts[:package] || executable
      Mix.raise(missing_package_message(package))
    end
  end

  defp missing_package_message(package) do
    """
    #{package} is required by the Nerves tooling.

    Please see https://hexdocs.pm/nerves/installation.html for installation
    instructions.
    """
  end
end
