defmodule Mix.Tasks.CopyMibs do
  @shortdoc "Copies MIB files from source directory to app MIB directory"

  @moduledoc """
  Copies MIB files from a source directory to the application's MIB directory.

  ## Usage

      # Copy all MIB files
      mix copy_mibs --source-path ~/mibs

      # Copy specific vendor MIB directories
      mix copy_mibs --source-path ~/mibs --vendors mikrotik,cisco,ubiquiti
  """

  use Mix.Task

  require Logger

  def run(args) do
    {opts, _} =
      OptionParser.parse!(args,
        strict: [
          source_path: :string,
          vendors: :string,
          target_dir: :string
        ]
      )

    source_path = opts[:source_path] || raise "Missing --source-path argument"
    target_dir = opts[:target_dir] || get_default_target_dir()
    vendors = parse_vendors(opts[:vendors])

    source_mibs = source_path

    if !File.exists?(source_mibs) do
      raise "LibreNMS MIB directory not found: #{source_mibs}"
    end

    # Ensure target directory exists
    File.mkdir_p!(target_dir)

    case vendors do
      :all ->
        copy_all_mibs(source_mibs, target_dir)

      vendor_list ->
        copy_vendor_mibs(source_mibs, target_dir, vendor_list)
    end
  end

  defp get_default_target_dir do
    # In development, use priv/mibs; in production use /app/mibs
    if Application.get_env(:towerops, :env) == :dev do
      Path.join([File.cwd!(), "priv", "mibs"])
    else
      Application.get_env(:towerops, :mib_dir, "/app/mibs")
    end
  end

  defp parse_vendors(nil), do: :all
  defp parse_vendors(""), do: :all

  defp parse_vendors(vendors_string) do
    vendors_string
    |> String.split(",")
    |> Enum.map(&String.trim/1)
    |> Enum.reject(&(&1 == ""))
  end

  defp copy_all_mibs(source_dir, target_dir) do
    Logger.info("Copying all MIB files from #{source_dir} to #{target_dir}...")

    # Use rsync for efficient copying
    case System.cmd("rsync", ["-av", "--exclude=.git", "#{source_dir}/", target_dir]) do
      {output, 0} ->
        Logger.info("Successfully copied MIB files")
        Logger.debug(output)
        :ok

      {error, exit_code} ->
        Logger.error("Failed to copy MIB files (exit code #{exit_code}): #{error}")
        {:error, :copy_failed}
    end
  rescue
    _e in ErlangError ->
      # rsync not found, fall back to File.cp_r
      Logger.warning("rsync not found, using File.cp_r (slower)")
      copy_mibs_recursive(source_dir, target_dir)
  end

  defp copy_vendor_mibs(source_dir, target_dir, vendors) do
    Logger.info("Copying MIB files for vendors: #{Enum.join(vendors, ", ")}")

    results =
      Enum.map(vendors, fn vendor ->
        source_path = Path.join(source_dir, vendor)
        target_path = Path.join(target_dir, vendor)

        if File.exists?(source_path) do
          Logger.info("Copying #{vendor} MIBs...")
          File.mkdir_p!(target_path)
          _ = File.cp_r!(source_path, target_path)
          {:ok, vendor}
        else
          Logger.warning("Vendor MIB directory not found: #{vendor}")
          {:error, vendor}
        end
      end)

    successes = Enum.count(results, &match?({:ok, _}, &1))
    failures = Enum.count(results, &match?({:error, _}, &1))

    Logger.info("Copied #{successes} vendor MIB directories (#{failures} not found)")
  end

  defp copy_mibs_recursive(source_dir, target_dir) do
    case File.cp_r(source_dir, target_dir) do
      {:ok, _files} ->
        Logger.info("Successfully copied MIB files")
        :ok

      {:error, reason, file} ->
        Logger.error("Failed to copy #{file}: #{inspect(reason)}")
        {:error, :copy_failed}
    end
  end
end
