defmodule Mix.Tasks.GenerateMockups do
  @moduledoc """
  Generates product mockups using Printify or Printful APIs.

  This task automates the creation of product mockups for the Berrypod
  sample content. It downloads artwork from Unsplash, uploads it to the
  print-on-demand provider, creates products, and downloads the generated mockups.

  ## Usage

      # Printify (default)
      mix generate_mockups
      mix generate_mockups --cleanup
      mix generate_mockups --replace

      # Printful
      mix generate_mockups --provider printful
      mix generate_mockups --provider printful --mockups-only
      mix generate_mockups --provider printful --products-only
      mix generate_mockups --provider printful --cleanup

  ## Output

  Mockup images are saved to: priv/static/mockups/
  """

  use Mix.Task

  alias Berrypod.Mockups.Generator, as: PrintifyGenerator
  alias Berrypod.Mockups.PrintfulGenerator

  @shortdoc "Generates product mockups using Printify or Printful API"

  @impl Mix.Task
  def run(args) do
    Mix.Task.run("app.start")

    {opts, _, _} =
      OptionParser.parse(args,
        switches: [
          cleanup: :boolean,
          replace: :boolean,
          search: :string,
          list_blueprints: :boolean,
          help: :boolean,
          provider: :string,
          mockups_only: :boolean,
          products_only: :boolean
        ],
        aliases: [
          c: :cleanup,
          r: :replace,
          s: :search,
          l: :list_blueprints,
          h: :help,
          p: :provider
        ]
      )

    provider = Keyword.get(opts, :provider, "printify")

    cond do
      opts[:help] ->
        print_help()

      provider == "printful" ->
        run_printful(opts)

      opts[:list_blueprints] ->
        list_blueprints()

      opts[:search] ->
        search_blueprints(opts[:search])

      true ->
        run_printify(opts)
    end
  end

  defp print_help do
    Mix.shell().info("""

    Mockup Generator
    ================

    Generates product mockups using Printify or Printful APIs.

    Usage:
      mix generate_mockups [options]

    Common options:
      --provider, -p NAME   Provider to use: "printify" (default) or "printful"
      --cleanup, -c         Delete created products after downloading mockups
      --help, -h            Show this help message

    Printify options:
      --replace, -r         Delete ALL existing products before generating (with confirmation)
      --search, -s TERM     Search for blueprints by name
      --list-blueprints     List all available blueprint IDs and names

    Printful options:
      --mockups-only        Only generate mockup images (skip product creation)
      --products-only       Only create sync products (skip mockup images)

    Environment:
      PRINTIFY_API_TOKEN    Required for Printify provider
      PRINTFUL_API_TOKEN    Required for Printful provider

    Examples:
      mix generate_mockups
      mix generate_mockups --provider printful
      mix generate_mockups --provider printful --mockups-only
      mix generate_mockups --provider printful --cleanup
    """)
  end

  defp list_blueprints do
    Mix.shell().info("Fetching blueprints from Printify...")

    case PrintifyGenerator.list_blueprints() do
      blueprints when is_list(blueprints) ->
        Mix.shell().info("\nAvailable Blueprints:\n")

        blueprints
        |> Enum.each(fn {id, title} ->
          Mix.shell().info("  #{id}: #{title}")
        end)

        Mix.shell().info("\nTotal: #{length(blueprints)} blueprints")

      {:error, reason} ->
        Mix.shell().error("Error fetching blueprints: #{inspect(reason)}")
    end
  end

  defp search_blueprints(term) do
    Mix.shell().info("Searching for blueprints matching '#{term}'...")

    case PrintifyGenerator.search_blueprints(term) do
      results when is_list(results) ->
        if results == [] do
          Mix.shell().info("No blueprints found matching '#{term}'")
        else
          Mix.shell().info("\nMatching Blueprints:\n")

          results
          |> Enum.each(fn {id, title} ->
            Mix.shell().info("  #{id}: #{title}")
          end)

          Mix.shell().info("\nFound: #{length(results)} blueprints")
        end

      {:error, reason} ->
        Mix.shell().error("Error searching blueprints: #{inspect(reason)}")
    end
  end

  # ===========================================================================
  # Printify
  # ===========================================================================

  defp run_printify(opts) do
    cleanup = Keyword.get(opts, :cleanup, false)
    replace = Keyword.get(opts, :replace, false)

    Mix.shell().info("""

    ╔═══════════════════════════════════════════╗
    ║     Printify Mockup Generator             ║
    ╠═══════════════════════════════════════════╣
    ║  Replace mode: #{if replace, do: "ON ", else: "OFF"}                        ║
    ║  Cleanup mode: #{if cleanup, do: "ON ", else: "OFF"}                        ║
    ╚═══════════════════════════════════════════╝

    """)

    case System.get_env("PRINTIFY_API_TOKEN") do
      nil ->
        Mix.shell().error("""
        Error: PRINTIFY_API_TOKEN environment variable is not set.

        Set it and retry:
          export PRINTIFY_API_TOKEN="your-token"
          mix generate_mockups
        """)

      _token ->
        if replace, do: purge_existing_printify_products()

        results = PrintifyGenerator.generate_all(cleanup: cleanup)

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

        Mix.shell().info("""

        Summary: #{successful} succeeded, #{failed} failed
        """)

        if failed > 0 do
          Mix.shell().error(
            "Some products failed to generate. Check the output above for details."
          )
        end
    end
  end

  defp purge_existing_printify_products do
    alias Berrypod.Clients.Printify, as: Client

    Mix.shell().info("Fetching existing products...")

    {:ok, shop_id} = Client.get_shop_id()
    {:ok, %{"data" => products}} = Client.list_products(shop_id)
    count = length(products)

    if count == 0 do
      Mix.shell().info("No existing products to delete.\n")
    else
      Mix.shell().info("Found #{count} existing products in Printify.")

      if Mix.shell().yes?("Delete all #{count} products before generating new ones?") do
        deleted = PrintifyGenerator.purge_all_products(shop_id)
        Mix.shell().info("Deleted #{deleted} products.\n")
      else
        Mix.shell().info("Skipping purge.\n")
      end
    end
  end

  # ===========================================================================
  # Printful
  # ===========================================================================

  defp run_printful(opts) do
    mockups_only = Keyword.get(opts, :mockups_only, false)
    products_only = Keyword.get(opts, :products_only, false)
    cleanup = Keyword.get(opts, :cleanup, false)

    do_mockups = !products_only
    do_products = !mockups_only

    Mix.shell().info("""

    ╔═══════════════════════════════════════════╗
    ║     Printful Mockup Generator             ║
    ╠═══════════════════════════════════════════╣
    ║  Mockups:  #{if do_mockups, do: "ON ", else: "OFF"}                          ║
    ║  Products: #{if do_products, do: "ON ", else: "OFF"}                          ║
    ║  Cleanup:  #{if cleanup, do: "ON ", else: "OFF"}                          ║
    ╚═══════════════════════════════════════════╝

    """)

    case System.get_env("PRINTFUL_API_TOKEN") do
      nil ->
        Mix.shell().error("""
        Error: PRINTFUL_API_TOKEN environment variable is not set.

        Set it and retry:
          export PRINTFUL_API_TOKEN="your-token"
          mix generate_mockups --provider printful
        """)

      _token ->
        results =
          PrintfulGenerator.generate_all(
            mockups: do_mockups,
            products: do_products,
            cleanup: cleanup
          )

        mockup_ok = Enum.count(results.mockups, &match?({:ok, _, _}, &1))
        mockup_fail = Enum.count(results.mockups, &match?({:error, _, _}, &1))
        product_ok = Enum.count(results.products, &match?({:ok, _, _}, &1))
        product_fail = Enum.count(results.products, &match?({:error, _, _}, &1))

        Mix.shell().info("""

        Summary:
          Mockups:  #{mockup_ok} succeeded, #{mockup_fail} failed
          Products: #{product_ok} succeeded, #{product_fail} failed
        """)

        if mockup_fail + product_fail > 0 do
          Mix.shell().error("Some operations failed. Check the output above for details.")
        end
    end
  end
end
