defmodule Mix.Tasks.AdventOfCode.Gen.Solution do
  use Mix.Task
  import Mix.Generator
  import Mix.AdventOfCode

  @shortdoc "Generate a new solution module"

  @moduledoc """
  #{@shortdoc}.

  Includes new solution module, test, and empty problem input.

  ## Examples

      # Generate solution modules, tests, and empty input for Day 2
      $ mix advent_of_code.gen.solution 2
  """

  @switches []

  @impl Mix.Task
  def run(args) do
    case OptionParser.parse!(args, switches: @switches) do
      {_, [day]} ->
        day_module = day_module(day)
        day_contents = day_template(mod: day_module) |> Code.format_string!()
        day_path = Path.join("lib", Macro.underscore(day_module))
        day_tests_path = Path.join("test", Macro.underscore(day_module))
        day_file = "#{day_path}.ex"

        create_directory(day_path)
        create_directory(day_tests_path)
        create_file(day_file, day_contents)

        for part <- 1..2 do
          part_module = part_module(day, part)

          part_contents =
            part_template(mod: part_module, day_mod: day_module) |> Code.format_string!()

          part_file = Path.join("lib", "#{Macro.underscore(part_module)}.ex")
          create_file(part_file, part_contents)

          part_test_module = Module.concat(day_module, Macro.camelize("Part#{part}Test"))

          part_test_contents =
            part_test_template(mod: part_module, test_mod: part_test_module)
            |> Code.format_string!()

          part_test_file =
            Path.join(day_tests_path, "#{Macro.underscore("part_#{part}_test")}.exs")

          create_file(part_test_file, part_test_contents)
        end

      _ ->
        Mix.raise("Unknown arguments.")
    end
  end

  embed_template(:day, ~S"""
    defmodule <%= inspect(@mod) %> do

    end
  """)

  embed_template(:part, ~S"""
    defmodule <%= inspect(@mod) %> do
      alias AdventOfCode.PuzzleSolver
      use PuzzleSolver

      import <%= inspect(@day_mod) %>, warn: false

      @impl PuzzleSolver
      def solve(_input_stream) do
        :ok |> to_string()
      end
    end
  """)

  embed_template(:part_test, ~S[
    defmodule <%= inspect(@test_mod) %> do
      use AdventOfCode.PuzzleCase, module: <%= inspect(@mod) %>

      test "returns :ok" do
        input = ~S"""
        input
        """

        assert_solution input, "ok"
      end
    end
  ])
end