defmodule Mix.Tasks.ImportProfiles do
  @moduledoc """
  Imports device SNMP profiles from external YAML files.

  ## Usage

      # Import a single profile
      mix import_profiles --profile epmp --detection ~/dev/librenms/resources/definitions/os_detection/epmp.yaml --discovery ~/dev/librenms/resources/definitions/os_discovery/epmp.yaml

      # Import specific profiles
      mix import_profiles --profiles epmp,airos-af,unifi

      # Import all profiles from external directory
      mix import_profiles --all --librenms-path ~/dev/librenms

  ## Options

      --profile NAME           - Import a single profile by name
      --profiles LIST          - Import multiple profiles (comma-separated)
      --all                    - Import all profiles from external
      --librenms-path PATH     - Path to external installation
      --detection PATH         - Path to detection YAML file
      --discovery PATH         - Path to discovery YAML file (optional)
      --force                  - Delete existing profile before importing

  """
  use Mix.Task

  alias Towerops.DeviceProfiles
  alias Towerops.DeviceProfiles.Importer
  alias Towerops.Repo

  @requirements ["app.start"]

  @impl Mix.Task
  def run(args) do
    {opts, _, _} =
      OptionParser.parse(args,
        strict: [
          profile: :string,
          profiles: :string,
          all: :boolean,
          librenms_path: :string,
          detection: :string,
          discovery: :string,
          force: :boolean
        ]
      )

    cond do
      opts[:all] ->
        import_all_profiles(opts)

      opts[:profiles] ->
        import_multiple_profiles(opts)

      opts[:profile] || opts[:detection] ->
        import_single_profile(opts)

      true ->
        print_usage()
    end
  end

  defp import_single_profile(opts) do
    detection_file = opts[:detection]
    discovery_file = opts[:discovery]
    force = opts[:force] || false

    if detection_file && File.exists?(detection_file) do
      os_name = Path.basename(detection_file, ".yaml")

      if force do
        delete_existing_profile(os_name)
      end

      Mix.shell().info("Importing profile: #{os_name}")

      case Importer.import_profile(detection_file, discovery_file) do
        {:ok, profile} ->
          Mix.shell().info("Successfully imported profile: #{profile.os}")
          print_profile_stats(profile)

        {:error, reason} ->
          Mix.shell().error("Failed to import profile: #{inspect(reason)}")
      end
    else
      Mix.shell().error("Detection file not found: #{detection_file}")
    end
  end

  defp import_multiple_profiles(opts) do
    profiles = String.split(opts[:profiles], ",", trim: true)
    librenms_path = opts[:librenms_path] || guess_librenms_path()
    force = opts[:force] || false

    if librenms_path do
      detection_dir = Path.join(librenms_path, "resources/definitions/os_detection")
      discovery_dir = Path.join(librenms_path, "resources/definitions/os_discovery")

      Enum.each(profiles, fn profile_name ->
        import_profile_by_name(profile_name, detection_dir, discovery_dir, force)
      end)
    else
      Mix.shell().error("external path not found. Use --librenms-path option.")
    end
  end

  defp import_all_profiles(opts) do
    librenms_path = opts[:librenms_path] || guess_librenms_path()
    force = opts[:force] || false

    if librenms_path do
      detection_dir = Path.join(librenms_path, "resources/definitions/os_detection")
      discovery_dir = Path.join(librenms_path, "resources/definitions/os_discovery")

      if force do
        Mix.shell().info("Deleting all existing profiles...")
        delete_all_profiles()
      end

      Mix.shell().info("Importing all profiles from: #{librenms_path}")
      Mix.shell().info("This may take several minutes...")

      {:ok, stats} = Importer.import_all_from_directory(detection_dir, discovery_dir)
      Mix.shell().info("\nImport complete!")
      Mix.shell().info("Successfully imported: #{stats.success} profiles")
      Mix.shell().info("Failed to import: #{stats.failed} profiles")
    else
      Mix.shell().error("external path not found. Use --librenms-path option.")
    end
  end

  defp import_profile_by_name(profile_name, detection_dir, discovery_dir, force) do
    detection_file = Path.join(detection_dir, "#{profile_name}.yaml")
    discovery_file = Path.join(discovery_dir, "#{profile_name}.yaml")

    discovery_file = if File.exists?(discovery_file), do: discovery_file

    if File.exists?(detection_file) do
      if force do
        delete_existing_profile(profile_name)
      end

      Mix.shell().info("Importing profile: #{profile_name}")

      case Importer.import_profile(detection_file, discovery_file) do
        {:ok, profile} ->
          Mix.shell().info("✓ Successfully imported: #{profile.os}")

        {:error, reason} ->
          Mix.shell().error("✗ Failed to import #{profile_name}: #{inspect(reason)}")
      end
    else
      Mix.shell().error("Detection file not found for: #{profile_name}")
    end
  end

  defp delete_existing_profile(os_name) do
    if profile = DeviceProfiles.get_profile(os_name) do
      Mix.shell().info("Deleting existing profile: #{os_name}")
      DeviceProfiles.delete_profile(profile)
    end
  end

  defp delete_all_profiles do
    profiles = DeviceProfiles.list_profiles()

    Enum.each(profiles, fn profile ->
      DeviceProfiles.delete_profile(profile)
    end)

    Mix.shell().info("Deleted #{length(profiles)} existing profiles")
  end

  defp guess_librenms_path do
    home = System.get_env("HOME")
    possible_paths = ["~/dev/librenms", "#{home}/dev/librenms", "/opt/librenms"]

    possible_paths
    |> Enum.find(fn path ->
      expanded = Path.expand(path)
      File.dir?(expanded)
    end)
    |> case do
      nil -> nil
      path -> Path.expand(path)
    end
  end

  defp print_profile_stats(profile) do
    profile = Repo.preload(profile, [:detection_rules, :sensor_definitions, :processor_definitions])

    Mix.shell().info("")
    Mix.shell().info("  OS: #{profile.os}")
    Mix.shell().info("  Text: #{profile.text}")
    Mix.shell().info("  Type: #{profile.type}")
    Mix.shell().info("  Detection rules: #{length(profile.detection_rules)}")
    Mix.shell().info("  Sensor definitions: #{length(profile.sensor_definitions)}")
    Mix.shell().info("  Processor definitions: #{length(profile.processor_definitions)}")
    Mix.shell().info("")
  end

  defp print_usage do
    Mix.shell().info("""
    Usage:
      mix import_profiles [options]

    Options:
      --profile NAME           Import a single profile by name
      --profiles LIST          Import multiple profiles (comma-separated)
      --all                    Import all profiles from external
      --librenms-path PATH     Path to external installation
      --detection PATH         Path to detection YAML file
      --discovery PATH         Path to discovery YAML file (optional)
      --force                  Delete existing profile before importing

    Examples:
      # Import specific profiles
      mix import_profiles --profiles epmp,airos-af,unifi --librenms-path ~/dev/librenms

      # Import a single profile with explicit paths
      mix import_profiles --detection ~/dev/librenms/resources/definitions/os_detection/epmp.yaml --discovery ~/dev/librenms/resources/definitions/os_discovery/epmp.yaml

      # Import all profiles (takes several minutes)
      mix import_profiles --all --librenms-path ~/dev/librenms
    """)
  end
end
