defmodule Mix.Tasks.Setup do
  @shortdoc "Sets up the project for local development"
  @moduledoc """
  Sets up the project for local development.

  This task:
  1. Installs Elixir dependencies
  2. Ensures Poetry is installed and configured
  3. Create virtual env
  4. Installs Python dependencies

  ## Usage

      mix setup
  """
  use Mix.Task

  @requirements []

  @priv_python_dir Application.app_dir(:lux, "priv/python")
  @priv_nodejs_dir Application.app_dir(:lux, "priv/node")

  defp safe_cmd(cmd, args, opts) do
    System.cmd(cmd, args, opts)
  rescue
    e in ErlangError ->
      case e do
        %ErlangError{original: :enoent} -> {:error, :not_found}
        _ -> reraise e, __STACKTRACE__
      end
  end

  @impl Mix.Task
  def run(opts \\ []) do
    install_deps? = Keyword.get(opts, :install_deps, true)

    Mix.shell().info("\n==> Setting up Lux for development\n")
    # Step 1: Install Elixir dependencies
    Mix.shell().info("==> Installing Elixir dependencies...")

    with :ok <- install_deps(install_deps?),
         # Step 2: Check and setup Poetry
         Mix.shell().info("\n==> Checking Poetry installation..."),
         {:ok, poetry_path} <- find_poetry(),
         # Step 3: Setup Python virtual environment
         Mix.shell().info("\n==> Setting up Python virtual environment..."),
         :ok <- setup_virtualenv(),
         # Step 4: Install Python dependencies
         Mix.shell().info("\n==> Installing Python dependencies..."),
         :ok <- install_python_deps(poetry_path),
         # Step 5: Install Node.js dependencies
         Mix.shell().info("\n==> Installing Node.js dependencies..."),
         :ok <- install_nodejs_deps() do
      Mix.shell().info("""
      \n==> Setup completed successfully! 🎉

      You can now:
      • Activate the Python virtual environment:
          source #{@priv_python_dir}/.venv/bin/activate

      • Run tests:
          mix test.unit         # Run Elixir unit tests
          mix test.integration  # Run Elixir integration tests
          mix python.test       # Run Python tests

      • Generate documentation:
          mix docs

      • Start development:
          iex -S mix
      """)
    else
      _ ->
        exit({:shutdown, 1})
    end
  end

  defp find_poetry do
    case safe_cmd("which", ["poetry"], stderr_to_stdout: true) do
      {poetry_path, 0} ->
        poetry_path = String.trim(poetry_path)
        Mix.shell().info("Found Poetry at: #{poetry_path}")
        {:ok, poetry_path}

      {:error, :not_found} ->
        Mix.shell().error("Poetry not found in system PATH")
        display_poetry_install_instructions()
        exit({:shutdown, 0})

      {_, _} ->
        Mix.shell().error("Poetry not found in system PATH")
        display_poetry_install_instructions()
        exit({:shutdown, 0})
    end
  end

  defp display_poetry_install_instructions do
    Mix.shell().error("""
    Please run:
      asdf plugin-add poetry
      asdf install poetry

    or try manually:

    1. Install Poetry:
        curl -sSL https://install.python-poetry.org | python3 -

    2. Add to your PATH by adding this line to ~/.zshrc or ~/.bashrc:
        export PATH="$HOME/.local/bin:$PATH"

    3. Reload your shell:
        source ~/.zshrc  # or source ~/.bashrc

    Then run `mix setup` again.
    """)
  end

  defp install_deps(false), do: :ok

  defp install_deps(_) do
    case safe_cmd("mix", ["deps.get"], into: IO.stream()) do
      {:error, :not_found} ->
        Mix.raise("Could not find 'mix' command")

      {_, 0} ->
        :ok

      {output, _} ->
        Mix.raise("Failed to install dependencies: #{output}")
    end
  end

  defp setup_virtualenv do
    venv_path = [@priv_python_dir, ".venv"] |> Path.join() |> Path.expand()

    with false <- File.exists?(venv_path),
         {output, 0} <- safe_cmd("python3", ["-m", "venv", venv_path], stderr_to_stdout: true) do
      Mix.shell().info(output)
      :ok
    else
      true ->
        Mix.shell().info("Python virtual environment at: #{venv_path}")
        :ok

      {:error, :not_found} ->
        Mix.shell().error("Python is required to setup the virtual environment")
        exit({:shutdown, 1})

      {output, _} ->
        Mix.shell().error("Failed to setup Python virtual environment: #{output}")
        exit({:shutdown, 1})
    end
  end

  defp install_python_deps(poetry_path) do
    case safe_cmd(poetry_path, ["install"], cd: @priv_python_dir, stderr_to_stdout: true) do
      {:error, :not_found} ->
        Mix.shell().error("\nFailed to execute Poetry command")

        Mix.shell().error("""

        Try these steps to resolve the issue:
        1. Make sure Poetry is properly installed:
            poetry --version
        2. If needed, update Poetry:
            poetry self update
        3. Try clearing Poetry's cache:
            poetry cache clear . --all
        4. Run setup again:
            mix setup
        """)

        exit({:shutdown, 1})

      {output, 0} ->
        Mix.shell().info(output)
        :ok

      {output, _} ->
        Mix.shell().error("\nFailed to install Python dependencies:\n\n#{output}")

        Mix.shell().error("""

        Try these steps to resolve the issue:
        1. Clear Poetry's cache:
            cd priv/python && poetry cache clear . --all
        2. Delete the poetry.lock file (if it exists):
            rm priv/python/poetry.lock
        3. Update Poetry itself:
            poetry self update
        4. Run setup again:
            mix setup

        If the issue persists, please check the error message above for specific dependency conflicts.
        You may need to manually resolve version conflicts in priv/python/pyproject.toml.
        """)

        exit({:shutdown, 1})
    end
  end

  defp install_nodejs_deps do
    case safe_cmd("npm", ["install"], cd: @priv_nodejs_dir, stderr_to_stdout: true) do
      {:error, :not_found} ->
        Mix.shell().error("\nFailed to execute npm command")

        Mix.shell().error("""

        Try these steps to resolve the issue:
        1. Make sure npm is properly installed:
            npm --version
        2. If needed, update npm:
            npm install -g npm
        3. Run setup again:
            mix setup
        """)

        exit({:shutdown, 1})

      {output, 0} ->
        Mix.shell().info(output)
        :ok

      {output, _} ->
        Mix.shell().error("\nFailed to install Node.js dependencies:\n\n#{output}")

        Mix.shell().error("""

        Try these steps to resolve the issue:
        1. Clear the node_modules directory:
            rm -rf priv/node/node_modules
        2. Run setup again:
            mix setup

        If the issue persists, please check the error message above for specific dependency conflicts.
        You may need to manually resolve version conflicts in priv/node/package.json.
        """)

        exit({:shutdown, 1})
    end
  end
end
