import os
import hashlib
import json
from datetime import datetime
from tqdm import tqdm
from colorama import Fore, Style, init

init(autoreset=True)


def file_sha1(path, block_size=65536):
    sha1 = hashlib.sha1()
    try:
        with open(path, "rb") as f:
            for block in iter(lambda: f.read(block_size), b""):
                sha1.update(block)
        return sha1.hexdigest()
    except Exception:
        return "ERROR_HASH"


def format_size(bytes_size):
    for unit in ["B", "KB", "MB", "GB"]:
        if bytes_size < 1024:
            return f"{bytes_size:.1f}{unit}"
        bytes_size /= 1024
    return f"{bytes_size:.1f}TB"


# ---------------------------------------------------------
# JSON COMPLETO (solo estructura)
# ---------------------------------------------------------
def build_json_tree(directory, max_depth=None):
    directory = os.path.abspath(directory)

    def walk(path, level=0):
        if max_depth is not None and level > max_depth:
            return {}

        tree = {}
        try:
            entries = sorted(os.listdir(path))
        except Exception:
            return tree

        for entry in entries:
            full_path = os.path.join(path, entry)
            if os.path.isdir(full_path):
                tree[entry] = walk(full_path, level + 1)
            else:
                tree[entry] = {}
        return tree

    root_name = os.path.basename(directory) or directory
    return {root_name: walk(directory)}


# ---------------------------------------------------------
# JSON SOLO HASTA NIVEL 3
# ---------------------------------------------------------
def build_json_tree_levels3(directory, max_depth=3):
    directory = os.path.abspath(directory)

    def walk(path, level=0):
        if level > max_depth:
            return {}

        tree = {}
        try:
            entries = sorted(os.listdir(path))
        except Exception:
            return tree

        for entry in entries:
            full_path = os.path.join(path, entry)
            if os.path.isdir(full_path):
                tree[entry] = walk(full_path, level + 1)
            else:
                if level <= max_depth:
                    tree[entry] = {}
        return tree

    root_name = os.path.basename(directory) or directory
    return {root_name: walk(directory)}


# ---------------------------------------------------------
# GENERADOR PRINCIPAL
# ---------------------------------------------------------
def generate_folder_tree(directory, output_file="utilidades_gestion/folder_tree.txt", max_depth=None):
    lines = []
    directory = os.path.abspath(directory)

    total_files = sum(len(files) for _, _, files in os.walk(directory))

    print(Fore.CYAN + f"\n📁 Escaneando directorio: {directory}")
    print(Fore.YELLOW + f"📦 Archivos totales detectados: {total_files}\n")

    global_bar = tqdm(total=total_files, desc="Progreso total", unit="file", colour="green")

    for root, dirs, files in os.walk(directory):
        level = root.replace(directory, "").count(os.sep)

        if max_depth is not None and level > max_depth:
            continue

        indent = " " * 4 * level
        folder_name = os.path.basename(root) or root

        folder_bar = tqdm(
            total=len(files),
            desc=f"{Fore.BLUE}📂 {folder_name}{Style.RESET_ALL}",
            unit="file",
            leave=False,
            colour="blue"
        )

        lines.append(f"{indent}{folder_name}/")

        if max_depth is None or level < max_depth:
            for f in files:
                file_path = os.path.join(root, f)

                try:
                    size = format_size(os.path.getsize(file_path))
                    mtime = datetime.fromtimestamp(os.path.getmtime(file_path)).strftime("%Y-%m-%d %H:%M")
                    sha1 = file_sha1(file_path)
                except Exception:
                    size = "ERROR"
                    mtime = "ERROR"
                    sha1 = "ERROR"

                file_indent = " " * 4 * (level + 1)
                ext = os.path.splitext(f)[1].lower() or "NO_EXT"

                lines.append(
                    f"{file_indent}{f}  "
                    f"(size={size}, modified={mtime}, type={ext}, sha1={sha1})"
                )

                folder_bar.update(1)
                global_bar.update(1)

        folder_bar.close()

    global_bar.close()

    # Guardar TXT
    os.makedirs(os.path.dirname(output_file), exist_ok=True)
    with open(output_file, "w", encoding="utf-8") as f:
        f.write("\n".join(lines))

    print(Fore.GREEN + f"\n📄 Archivo TXT generado: {output_file}")

    # Guardar JSON completo
    json_tree = build_json_tree(directory, max_depth=max_depth)
    json_path = output_file.replace(".txt", ".json")

    with open(json_path, "w", encoding="utf-8") as jf:
        json.dump(json_tree, jf, indent=4, ensure_ascii=False)

    print(Fore.GREEN + f"📄 Archivo JSON completo generado: {json_path}")

    # Guardar JSON niveles 0–3
    json_tree_levels3 = build_json_tree_levels3(directory, max_depth=3)
    json_path_levels3 = output_file.replace(".txt", "_levels3.json")

    with open(json_path_levels3, "w", encoding="utf-8") as jf3:
        json.dump(json_tree_levels3, jf3, indent=4, ensure_ascii=False)

    print(Fore.GREEN + f"📄 Archivo JSON (niveles 0–3) generado: {json_path_levels3}")
    print(Fore.MAGENTA + "✨ Proceso completado.\n")


if __name__ == "__main__":
    project_path = "."
    generate_folder_tree(project_path, max_depth=None)