defmodule Ascii.ArtGenerator do
  @moduledoc """
  Generates ASCII art from text using various fonts.
  """

  @fonts %{
    "standard" => %{
      "A" => ["  /\\  ", " /  \\ ", "/____\\"],
      "B" => ["____  ", "|  _ \\ ", "|____/ "],
      "C" => [" ____ ", "/  __\\", "\\____/"],
      "D" => ["____  ", "|  \\ \\", "|___\\/"],
      "E" => ["_____ ", "|  ___", "|____\\"],
      "F" => ["_____ ", "|  ___", "|_|   "],
      "G" => [" ____ ", "/  __\\", "\\____|"],
      "H" => ["|   | ", "|___| ", "|   | "],
      "I" => ["_____ ", "  |   ", "__|__ "],
      "J" => ["_____ ", "    | ", "\\___/ "],
      "K" => ["|  /  ", "|_/   ", "| \\   "],
      "L" => ["|     ", "|     ", "|____ "],
      "M" => ["|\\ /| ", "| V | ", "|   | "],
      "N" => ["|\\ |  ", "| \\|  ", "|  |  "],
      "O" => [" ___  ", "/   \\ ", "\\___/ "],
      "P" => ["____  ", "|   \\ ", "|__|  "],
      "Q" => [" ___  ", "/   \\ ", "\\_\\_/"],
      "R" => ["____  ", "|   \\ ", "|_|\\ "],
      "S" => [" ___  ", "/ __\\ ", "\\___/ "],
      "T" => ["_____ ", "  |   ", "  |   "],
      "U" => ["|   | ", "|   | ", "\\___/ "],
      "V" => ["\\   / ", " \\ /  ", "  V   "],
      "W" => ["| | | ", "| | | ", "\\_|_/ "],
      "X" => ["\\  /  ", " ><   ", "/  \\  "],
      "Y" => ["\\  /  ", " \\/   ", " |    "],
      "Z" => ["____  ", "  /   ", " /__  "],
      " " => ["  ", "  ", "  "],
      "!" => ["| ", "| ", ". "],
      "?" => [" __ ", "  _|", " .  "],
      "0" => [" _ ", "| |", "|_|"],
      "1" => ["   ", " /|", " | "],
      "2" => [" _ ", " _|", "|_ "],
      "3" => [" _ ", " _|", " _|"],
      "4" => ["   ", "|_|", "  |"],
      "5" => [" _ ", "|_ ", " _|"],
      "6" => [" _ ", "|_ ", "|_|"],
      "7" => [" _ ", "  |", "  |"],
      "8" => [" _ ", "|_|", "|_|"],
      "9" => [" _ ", "|_|", " _|"]
    },
    "slant" => %{
      "A" => ["    ___   ", "   / _ |  ", "  /_/ |_| "],
      "B" => ["   ___    ", "  / _ )   ", " /____/   "],
      "C" => ["   ______", "  / ____/", " /_/     "],
      "D" => ["    ___  ", "   / _ \\ ", "  /____/ "],
      "E" => ["   ____  ", "  / __/  ", " /____/  "],
      "F" => ["   ____  ", "  / __/  ", " /_/     "],
      "G" => ["   ______", "  / ____/", " /_/|___|"],
      "H" => ["    __ __ ", "   / // / ", "  /_//_/  "],
      "I" => ["   ____ ", "  /  _/ ", " /_/    "],
      "J" => ["      __ ", "  __ / / ", " /___|/  "],
      "K" => ["    __ __", "   / //_/", "  /_,_/  "],
      "L" => ["    __   ", "   / /   ", "  /_/__  "],
      "M" => ["    __  ___", "   /  |/  /", "  /_/|_/_/ "],
      "N" => ["    _  __ ", "   / |/ / ", "  /_/|_/  "],
      "O" => ["   ____  ", "  / __ \\ ", " /_____/ "],
      "P" => ["   ____  ", "  / __ \\ ", " /_/     "],
      "Q" => ["   ____  ", "  / __ \\ ", " /_/\\_\\/"],
      "R" => ["   ____  ", "  / __ \\ ", " /_/ |_| "],
      "S" => ["   ____ ", "  / __/ ", " /____/ "],
      "T" => ["  ______ ", " /_  __/ ", "  /_/    "],
      "U" => ["    __  __ ", "   / / / / ", "  /_/_/_/  "],
      "V" => ["  _    __ ", " | |  / / ", " |___/_/  "],
      "W" => ["  _      __", " | | /| / /", " |_|/ |__/ "],
      "X" => ["    _  __ ", "   | |/_/ ", "  /_/|_|  "],
      "Y" => ["  __  __  ", " \\ \\/ /  ", "  /_/     "],
      "Z" => ["   ____ ", "  /_  / ", " /____/ "],
      " " => ["  ", "  ", "  "]
    },
    "3d" => %{
      "A" => [
        " ________     ",
        "|\\   __  \\   ",
        "\\ \\  \\|\\  \\  ",
        " \\ \\   __  \\ ",
        "  \\ \\  \\ \\  \\",
        "   \\ \\__\\ \\__\\",
        "    \\|__|\\|__|"
      ],
      "B" => [
        " ________     ",
        "|\\   __  \\   ",
        "\\ \\  \\|\\ /_  ",
        " \\ \\   __  \\ ",
        "  \\ \\  \\|\\  \\",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "C" => [
        " ________     ",
        "|\\   ____\\   ",
        "\\ \\  \\___|   ",
        " \\ \\  \\      ",
        "  \\ \\  \\____  ",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "D" => [
        " ________     ",
        "|\\   ___ \\   ",
        "\\ \\  \\_|\\ \\  ",
        " \\ \\  \\ \\\\ \\ ",
        "  \\ \\  \\_\\\\ \\",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "E" => [
        " _______      ",
        "|\\  ___ \\    ",
        "\\ \\   __/|   ",
        " \\ \\  \\_|/__ ",
        "  \\ \\  \\_|\\ \\",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "F" => [
        " ________     ",
        "|\\  _____\\   ",
        "\\ \\  \\___/   ",
        " \\ \\   __\\   ",
        "  \\ \\  \\_|   ",
        "   \\ \\__\\     ",
        "    \\|__|     "
      ],
      "G" => [
        " ________     ",
        "|\\   ____\\   ",
        "\\ \\  \\___|   ",
        " \\ \\  \\  ___ ",
        "  \\ \\  \\|\\  \\",
        "   \\ \\__\\ \\__\\",
        "    \\|__|\\|__|"
      ],
      "H" => [
        " ___  ___    ",
        "|\\  \\|\\  \\  ",
        "\\ \\  \\\\\\  \\ ",
        " \\ \\   __  \\ ",
        "  \\ \\  \\ \\  \\",
        "   \\ \\__\\ \\__\\",
        "    \\|__|\\|__|"
      ],
      "I" => [
        " ___      ",
        "|\\  \\     ",
        "\\ \\  \\    ",
        " \\ \\  \\   ",
        "  \\ \\  \\  ",
        "   \\ \\__\\ ",
        "    \\|__| "
      ],
      "J" => [
        "       ___ ",
        "      |\\  \\",
        "      \\ \\  \\",
        " __   \\ \\  \\",
        "|\\  \\  \\ \\  \\",
        "\\ \\  \\__\\ \\__\\",
        " \\|__|\\__|\\|__|"
      ],
      "K" => [
        " ___  __    ",
        "|\\  \\|\\  \\  ",
        "\\ \\  \\/  /|_",
        " \\ \\   ___  \\",
        "  \\ \\  \\\\ \\  \\",
        "   \\ \\__\\\\ \\__\\",
        "    \\|__| \\|__|"
      ],
      "L" => [
        " ___       ",
        "|\\  \\      ",
        "\\ \\  \\     ",
        " \\ \\  \\    ",
        "  \\ \\  \\____",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "M" => [
        " _____ ___   ",
        "|\\   _ \\  \\  ",
        "\\ \\  \\\\\\  \\ ",
        " \\ \\  \\\\|__| ",
        "  \\ \\  \\      ",
        "   \\ \\__\\     ",
        "    \\|__|     "
      ],
      "N" => [
        " ___   ___ ",
        "|\\  \\ |\\  \\",
        "\\ \\  \\\\ \\  \\",
        " \\ \\  \\\\ \\  \\",
        "  \\ \\  \\\\ \\  \\",
        "   \\ \\__\\\\ \\__\\",
        "    \\|__| \\|__|"
      ],
      "O" => [
        " ________     ",
        "|\\   __  \\   ",
        "\\ \\  \\|\\  \\  ",
        " \\ \\  \\\\\\  \\ ",
        "  \\ \\  \\\\\\  \\",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "P" => [
        " ________     ",
        "|\\   __  \\   ",
        "\\ \\  \\|\\  \\  ",
        " \\ \\   ____\\ ",
        "  \\ \\  \\___|  ",
        "   \\ \\__\\     ",
        "    \\|__|     "
      ],
      "Q" => [
        " ________     ",
        "|\\   __  \\   ",
        "\\ \\  \\|\\  \\  ",
        " \\ \\  \\\\\\  \\ ",
        "  \\ \\  \\\\\\  \\",
        "   \\ \\_____ \\ \\",
        "    \\|_____|\\__\\"
      ],
      "R" => [
        " ________     ",
        "|\\   __  \\   ",
        "\\ \\  \\|\\  \\  ",
        " \\ \\   _  _\\ ",
        "  \\ \\  \\\\  \\| ",
        "   \\ \\__\\\\ _\\ ",
        "    \\|__|\\|__|"
      ],
      "S" => [
        " ________     ",
        "|\\   ____\\   ",
        "\\ \\  \\___|_  ",
        " \\ \\_____  \\ ",
        "  \\|____|\\  \\",
        "    ____\\_\\  \\",
        "   |\\_________\\",
        "   \\|_________|"
      ],
      "T" => [
        " _________   ",
        "\\___   ___\\ ",
        "\\|___ \\___|  ",
        "     \\ \\  \\  ",
        "      \\ \\  \\ ",
        "       \\ \\__\\",
        "        \\|__|"
      ],
      "U" => [
        " ___  ___    ",
        "|\\  \\|\\  \\  ",
        "\\ \\  \\\\ \\  \\ ",
        " \\ \\  \\\\ \\  \\",
        "  \\ \\  \\\\ \\  \\",
        "   \\ \\_______\\",
        "    \\|_______|"
      ],
      "V" => [
        " ___      ___ ",
        "|\\  \\    /  /|",
        "\\ \\  \\  /  / /",
        " \\ \\  \\/  / / ",
        "  \\ \\    / /  ",
        "   \\ \\__/ /   ",
        "    \\|__|/    "
      ],
      "W" => [
        " ___   ___   ___ ",
        "|\\  \\ |\\  \\ |\\  \\",
        "\\ \\  \\\\ \\  \\\\ \\  \\",
        " \\ \\  \\\\ \\  \\\\ \\  \\",
        "  \\ \\  \\\\ \\  \\\\ \\  \\",
        "   \\ \\__\\\\ \\__\\\\ \\__\\",
        "    \\|__| \\|__| \\|__|"
      ],
      "X" => [
        " ___    ___ ",
        "|\\  \\  /  /|",
        "\\ \\  \\/  / /",
        " \\ \\    / / ",
        "  /  /\\   \\  ",
        " /  /  \\___\\ ",
        "|__|   |___| "
      ],
      "Y" => [
        " ___    ___ ",
        "|\\  \\  /  /|",
        "\\ \\  \\/  / /",
        " \\ \\    / / ",
        "  \\ \\  / /  ",
        "   \\ \\/ /   ",
        "    \\__/    "
      ],
      "Z" => [
        " ________ ",
        "|\\___   _\\",
        "\\|___ \\_|",
        "     \\ \\  \\",
        "      \\ \\  \\",
        "       \\ \\__\\",
        "        \\|__|"
      ],
      " " => ["   ", "   ", "   ", "   ", "   ", "   ", "   "]
    },
    "banner" => %{
      "A" => [" ##### ", "#     #", "#######", "#     #", "#     #"],
      "B" => ["###### ", "#     #", "###### ", "#     #", "###### "],
      "C" => [" ##### ", "#     #", "#      ", "#     #", " ##### "],
      "D" => ["###### ", "#     #", "#     #", "#     #", "###### "],
      "E" => ["#######", "#      ", "#####  ", "#      ", "#######"],
      "F" => ["#######", "#      ", "#####  ", "#      ", "#      "],
      "G" => [" ##### ", "#     #", "#      ", "#  ####", " ##### "],
      "H" => ["#     #", "#     #", "#######", "#     #", "#     #"],
      "I" => ["  ###  ", "   #   ", "   #   ", "   #   ", "  ###  "],
      "J" => ["      #", "      #", "      #", "#     #", " ##### "],
      "K" => ["#    # ", "#   #  ", "####   ", "#   #  ", "#    # "],
      "L" => ["#      ", "#      ", "#      ", "#      ", "#######"],
      "M" => ["#     #", "##   ##", "# # # #", "#  #  #", "#     #"],
      "N" => ["#     #", "##    #", "# #   #", "#  #  #", "#   ###"],
      "O" => [" ##### ", "#     #", "#     #", "#     #", " ##### "],
      "P" => ["###### ", "#     #", "###### ", "#      ", "#      "],
      "Q" => [" ##### ", "#     #", "#     #", "#   # #", " #### #"],
      "R" => ["###### ", "#     #", "###### ", "#   #  ", "#    # "],
      "S" => [" ##### ", "#     #", " ##### ", "      #", "###### "],
      "T" => ["#######", "   #   ", "   #   ", "   #   ", "   #   "],
      "U" => ["#     #", "#     #", "#     #", "#     #", " ##### "],
      "V" => ["#     #", "#     #", "#     #", " #   # ", "   #   "],
      "W" => ["#     #", "#  #  #", "# # # #", "##   ##", "#     #"],
      "X" => ["#     #", " #   # ", "   #   ", " #   # ", "#     #"],
      "Y" => ["#     #", " #   # ", "   #   ", "   #   ", "   #   "],
      "Z" => ["#######", "     # ", "   #   ", " #     ", "#######"],
      " " => ["  ", "  ", "  ", "  ", "  "]
    }
  }

  @doc """
  Generate ASCII art for the given text using the specified font.
  """
  def generate(text, font_name) when is_binary(text) and is_binary(font_name) do
    font = Map.get(@fonts, font_name, @fonts["standard"])

    text
    |> String.upcase()
    |> String.graphemes()
    |> Enum.map(&get_char_art(&1, font))
    |> transpose_chars()
    |> Enum.join("\n")
    |> then(&{:ok, &1})
  rescue
    _ -> {:error, "Failed to generate ASCII art"}
  end

  @doc """
  List all available fonts.
  """
  def list_fonts do
    Map.keys(@fonts)
  end

  @doc """
  Generate a simple text banner with borders.
  """
  def generate_banner(text, width \\ 60) do
    padding = 2
    text_lines = wrap_text(text, width - padding * 2 - 2)

    border = String.duplicate("=", width)
    empty_line = "|" <> String.duplicate(" ", width - 2) <> "|"

    lines = [
      border,
      empty_line
    ]

    lines =
      lines ++
        Enum.map(text_lines, fn line ->
          padded_line = String.pad_trailing(line, width - padding * 2 - 2)

          "|" <>
            String.duplicate(" ", padding) <> padded_line <> String.duplicate(" ", padding) <> "|"
        end)

    lines =
      lines ++
        [
          empty_line,
          border
        ]

    Enum.join(lines, "\n")
  end

  # Private functions

  defp get_char_art(char, font) do
    Map.get(font, char, font[" "])
  end

  defp transpose_chars(char_arts) do
    max_height =
      char_arts
      |> Enum.map(&length/1)
      |> Enum.max(fn -> 0 end)

    0..(max_height - 1)
    |> Enum.map(fn row ->
      char_arts
      |> Enum.map(fn char_lines ->
        if char_lines do
          Enum.at(char_lines, row, "   ")
        else
          "   "
        end
      end)
      |> Enum.join(" ")
    end)
  end

  defp wrap_text(text, max_width) do
    words = String.split(text)

    {lines, current_line} =
      Enum.reduce(words, {[], ""}, fn word, {lines, current} ->
        if String.length(current) == 0 do
          {lines, word}
        else
          test_line = current <> " " <> word

          if String.length(test_line) <= max_width do
            {lines, test_line}
          else
            {lines ++ [current], word}
          end
        end
      end)

    if current_line != "" do
      lines ++ [current_line]
    else
      lines
    end
  end
end
