defmodule Mix.Tasks.ExportProfiles do
  @moduledoc """
  Exports device SNMP profiles to JSON format for uploading to production.

  ## Usage

      # Export all profiles to JSON
      mix export_profiles --librenms-path ~/dev/librenms --output profiles.json

      # Export specific profiles
      mix export_profiles --librenms-path ~/dev/librenms --profiles epmp,airos-af --output profiles.json

  ## Options

      --librenms-path PATH     - Path to external installation (required)
      --output PATH            - Output JSON file path (default: profiles.json)
      --profiles LIST          - Export only specific profiles (comma-separated, optional)

  ## Output Format

  The exported JSON file contains all profile data in a format that can be uploaded
  to the production server via API:

      POST /api/v1/admin/profiles/import
      Content-Type: application/json
      Authorization: Bearer YOUR_TOKEN

  """
  use Mix.Task

  @requirements ["app.start"]

  @impl Mix.Task
  def run(args) do
    {opts, _, _} =
      OptionParser.parse(args,
        strict: [
          librenms_path: :string,
          output: :string,
          profiles: :string
        ]
      )

    librenms_path = opts[:librenms_path] || guess_librenms_path()
    output_file = opts[:output] || "profiles.json"
    profile_filter = parse_profile_filter(opts[:profiles])

    if librenms_path do
      Mix.shell().info("Exporting profiles from: #{librenms_path}")
      export_profiles(librenms_path, output_file, profile_filter)
    else
      Mix.shell().error("Please specify --librenms-path option")
      print_usage()
    end
  end

  defp export_profiles(librenms_path, output_file, profile_filter) do
    detection_dir = Path.join(librenms_path, "resources/definitions/os_detection")
    discovery_dir = Path.join(librenms_path, "resources/definitions/os_discovery")

    detection_files = Path.wildcard(Path.join(detection_dir, "*.yaml"))

    # Filter profiles if specified
    detection_files =
      if profile_filter do
        Enum.filter(detection_files, fn file ->
          os_name = Path.basename(file, ".yaml")
          os_name in profile_filter
        end)
      else
        detection_files
      end

    Mix.shell().info("Found #{length(detection_files)} profiles to export")
    Mix.shell().info("Reading YAML files...")

    # Read and parse all profiles
    profiles =
      detection_files
      |> Enum.map(fn detection_file ->
        os_name = Path.basename(detection_file, ".yaml")
        discovery_file = Path.join(discovery_dir, "#{os_name}.yaml")

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

        detection_data = read_yaml_file(detection_file)

        discovery_data =
          if File.exists?(discovery_file) do
            read_yaml_file(discovery_file)
          end

        %{
          os: os_name,
          detection: detection_data,
          discovery: discovery_data
        }
      end)
      |> Enum.reject(&is_nil(&1.detection))

    Mix.shell().info("\nWriting to: #{output_file}")

    # Write JSON file
    json_data = Jason.encode!(profiles, pretty: true)
    File.write!(output_file, json_data)

    file_size = format_bytes(File.stat!(output_file).size)
    Mix.shell().info("✓ Exported #{length(profiles)} profiles (#{file_size})")
    Mix.shell().info("\nUpload to production (requires superuser API token):")

    Mix.shell().info("""

        curl -X POST https://towerops.net/api/v1/profiles/import \\
          -H "Authorization: Bearer YOUR_SUPERUSER_API_TOKEN" \\
          -H "Content-Type: application/json" \\
          -d @#{output_file}

    Note: You must create an API token using a superuser account.
    Only tokens created by superusers can import device profiles.
    """)
  end

  defp read_yaml_file(file_path) do
    case YamlElixir.read_from_file(file_path) do
      {:ok, data} ->
        data

      {:error, reason} ->
        Mix.shell().error("  Failed to read #{file_path}: #{inspect(reason)}")
        nil
    end
  end

  defp parse_profile_filter(nil), do: nil

  defp parse_profile_filter(profiles_string) do
    profiles_string
    |> String.split(",", trim: true)
    |> Enum.map(&String.trim/1)
  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 format_bytes(bytes) when bytes < 1024, do: "#{bytes} B"
  defp format_bytes(bytes) when bytes < 1024 * 1024, do: "#{Float.round(bytes / 1024, 1)} KB"
  defp format_bytes(bytes), do: "#{Float.round(bytes / (1024 * 1024), 1)} MB"

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

    Options:
      --librenms-path PATH     Path to external installation (required)
      --output PATH            Output JSON file (default: profiles.json)
      --profiles LIST          Export specific profiles (comma-separated)

    Examples:
      # Export all profiles
      mix export_profiles --librenms-path ~/dev/librenms --output profiles.json

      # Export specific profiles
      mix export_profiles --librenms-path ~/dev/librenms --profiles epmp,unifi,airos-af
    """)
  end
end
