/*
 * src/commands/ls/helpers/printer.cpp
 * include/commands/ls/helpers/printer.h
 *
 * An helper to print the directory entries
 *
 * Copyright (C) 2026 Giuliano De Amicis. All rights reserved.
 * This software is licensed under the GPL-3.0-or-later.
 * See LICENSE file for details.
 */

#include "commands/ls/helpers/printer.h"

#include <algorithm>
#include <filesystem>
#include <ftxui/dom/elements.hpp>
#include <ftxui/dom/table.hpp>
#include <ftxui/screen/screen.hpp>
#include <ftxui/screen/terminal.hpp>
#include <iostream>
#include <string>
#include <vector>

#include "fs/DirectoryItem.h"
#include "fs/operations.h"
#include "ftxui/dom/elements.hpp"
#include "ftxui/dom/node.hpp"
#include "ftxui/screen/color.hpp"
#include "theme.h"

namespace fima {

namespace ls {

namespace helpers {

using namespace ftxui;

// what the actual fuck is this function, I don't understand nothing
void
print_normal(const std::vector<fima::fs::DirectoryItem>& items,
             const bool& icons,
             const bool& relative_paths)
{
    int max_width = 0;

    for (const auto& item : items) {
        max_width =
          std::max(max_width,
                   static_cast<int>((relative_paths ? item.get_parent_path_relative().size() : 0) +
                                    item.get_name(icons).size()));
    }

    int term_width = Terminal::Size().dimx;
    int cols       = std::max(2, term_width / max_width);
    int rows       = (items.size() + cols - 1) / cols;

    std::vector<std::vector<std::string>> grid_string(rows);
    std::vector<std::vector<Element>> grid(rows);

    for (size_t i = 0; i < items.size(); ++i) {
        size_t row = i % rows;

        std::string element_string =
          (relative_paths ? items[i].get_parent_path_relative() : "") + items[i].get_name(icons);

        grid_string[row].push_back(element_string);
    }

    auto get_max_of_column = [&](int column) {
        int max_length = 0;

        for (size_t row = 0; row < grid_string.size(); ++row) {
            if (column < grid_string[row].size()) {
                max_length =
                  std::max(max_length, static_cast<int>(grid_string[row][column].size()));
            }
        }

        return max_length;
    };

    for (size_t i = 0; i < items.size(); ++i) {
        size_t row = i % rows;
        size_t col = i / rows;

        std::string element_string = items[i].get_name(false);

        int full_size =
          element_string.size() + (relative_paths ? items[i].get_parent_path_relative().size() : 0);

        element_string +=
          std::string(std::max(full_size, get_max_of_column(col)) - full_size + 2, ' ');

        Element element =
          hbox(text((icons ? items[i].get_icon() + " " : "")),
               (relative_paths ? text(items[i].get_parent_path_relative()) |
                                   color(fima::theme::theme.directory.get_color_for_tui())
                               : text("")),
               text(element_string) | color(items[i].get_color().get_color_for_tui()) |
                 (items[i].is_directory() ||
                      fima::fs::operations::is_file_executable(items[i].get_path())
                    ? bold
                    : nothing));

        grid[row].push_back(element);
    }

    auto document = gridbox(grid);
    auto screen   = Screen::Create(Dimension::Full(), Dimension::Fixed(rows));
    Render(screen, document);
    screen.Print();

    std::cout << '\n';
}

void
print_one_line(const std::vector<fima::fs::DirectoryItem>& items,
               const bool& icons,
               const bool& relative_paths)
{
    std::vector<Element> vertical_elements{};

    for (auto& item : items) {
        // i'm getting crazy
        vertical_elements.push_back(hbox(
          text((icons ? item.get_icon() + " " : "")) | color(item.get_color().get_color_for_tui()),
          (relative_paths ? text(item.get_parent_path_relative()) |
                              color(fima::theme::theme.directory.get_color_for_tui())
                          : text("")),
          text(item.get_name(false)) | color(item.get_color().get_color_for_tui()) |
            (item.is_directory() || fima::fs::operations::is_file_executable(item.get_path())
               ? bold
               : nothing)));
    }

    auto document = vbox(vertical_elements);
    auto screen   = Screen::Create(Dimension::Fit(document), Dimension::Fixed(items.size()));
    Render(screen, document);
    screen.Print();

    std::cout << '\n';
}

void
print_long(std::vector<fima::fs::DirectoryItem>& items,
           const bool& icons,
           const bool& verbose,
           const bool& headers,
           const bool& relative_paths)
{
    std::vector<std::vector<Element>> table_data{};

    if (headers) {
        table_data.push_back(
          { text("Permissions ") | color(fima::theme::theme.ls_permissions.get_color_for_tui()) |
              bold,
            text("Size ") | color(fima::theme::theme.ls_size.get_color_for_tui()) | bold |
              align_right,
            text("User ") | color(fima::theme::theme.ls_user.get_color_for_tui()) | bold,
            text("Date Modified ") |
              color(fima::theme::theme.ls_date_modified.get_color_for_tui()) | bold,
            text("Name ") | color(fima::theme::theme.ls_name.get_color_for_tui()) | bold });
    }

    for (fima::fs::DirectoryItem& item : items) {
        Color size_color{ fima::theme::theme.info.get_color_for_tui() };
        Decorator size_decorator{ nothing };

        item.set_size();

        std::string size{ item.get_size_with_extension() };

        switch (*(size.rbegin() + 1)) {
            case 'K':
                size_decorator = bold;
                break;
            case 'M':
                size_color = fima::theme::theme.warning.get_color_for_tui();
                break;
            case 'G':
                size_color     = fima::theme::theme.warning.get_color_for_tui();
                size_decorator = bold;
                break;
            case 'T':
                size_color = fima::theme::theme.error
                               .get_color_for_tui(); // we use error because i think it's the best
                                                     // one for this kind of info
                break;
            case 'E':
            case 'P':
                size_color     = fima::theme::theme.error.get_color_for_tui();
                size_decorator = bold;
        }

        // what the heck is that
        table_data.push_back(
          { hbox(item.get_permissions_tui(), text(" ")),
            text(size + " ") | color(size_color) | size_decorator | align_right,
            text(item.get_owner() + " ") | color(fima::theme::theme.ls_user.get_color_for_tui()),
            text(item.get_last_modification_date() + " ") |
              color(fima::theme::theme.ls_date_modified.get_color_for_tui()),
            hbox(text(item.get_icon() + " ") | color(item.get_color().get_color_for_tui()),
                 (relative_paths ? text(item.get_parent_path_relative()) |
                                     color(fima::theme::theme.directory.get_color_for_tui())
                                 : text("")),
                 text(item.get_name(false)) | color(item.get_color().get_color_for_tui()) |
                   (fima::fs::operations::is_file_executable(item.get_path()) ||
                        std::filesystem::is_directory(item.get_path())
                      ? bold
                      : nothing),
                 text((item.is_symlink() ? " -> " + std::string(item.get_symlink_target()) : "")) |
                   color(item.get_color().get_color_for_tui())) });
    }

    Table table = Table({ table_data });

    int table_size{};

    table_size = table_data.size();

    Element document = table.Render();
    Screen screen = ftxui::Screen::Create(Dimension::Fit(document), Dimension::Fixed(table_size));
    Render(screen, document);
    screen.Print();

    std::cout << '\n';

    if (verbose) {
        int number_of_files       = 0;
        int number_of_directories = 0;

        for (const fima::fs::DirectoryItem& item : items) {
            if (item.is_directory()) {
                ++number_of_directories;
            } else if (item.is_file()) {
                ++number_of_files;
            }
        }

        std::cout << fima::theme::theme.primary << "files: " << fima::theme::theme.secondary
                  << number_of_files << fima::theme::theme.primary << ", ";
        std::cout << "directories: " << fima::theme::theme.secondary << number_of_directories
                  << fima::theme::Color::reset << '\n';
    }
}

} // namespace helpers

} // namespace ls

} // namespace fima
