<?php

declare(strict_types=1);

namespace App\Framework\Debug;

use App\Framework\Debug\Formatters\FormatterInterface;

/**
 * Hauptklasse für das Framework-weite Debugging.
 * Bietet eine übersichtlichere Alternative zu var_dump.
 */
final class Debugger
{
    /** @var bool */
    private static bool $enabled = true;

    /** @var OutputInterface|null */
    private static ?OutputInterface $output = null;

    /** @var FormatterInterface|null */
    private static ?FormatterInterface $formatter = null;

    /** @var DebugRegistry */
    private static ?DebugRegistry $registry = null;

    /**
     * Hauptfunktion für das Debugging - übersichtlichere Alternative zu var_dump
     */
    public static function dump(mixed $data, string $label = '', bool $die = false): void
    {
        if (! self::isEnabled()) {
            return;
        }

        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
        $caller = $backtrace[0] ?? [];

        $debugInfo = new DebugEntry(
            $data,
            $label,
            $caller['file'] ?? 'unknown',
            $caller['line'] ?? 0
        );

        self::getRegistry()->addEntry($debugInfo);
        self::getOutput()->output($debugInfo, self::getFormatter());

        if ($die) {
            die(1);
        }
    }

    /**
     * Spezielle Funktion für Arrays mit Formatierung
     */
    public static function dumpArray(array $data, string $label = 'Array Debug'): void
    {
        if (! self::isEnabled()) {
            return;
        }

        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
        $caller = $backtrace[0] ?? [];

        $debugInfo = new DebugEntry(
            $data,
            $label ?: 'Array Debug',
            $caller['file'] ?? 'unknown',
            $caller['line'] ?? 0,
            EntryType::ARRAY
        );

        self::getRegistry()->addEntry($debugInfo);
        self::getOutput()->output($debugInfo, self::getFormatter());
    }

    /**
     * Debug für Objekte mit Klasseninformationen
     */
    public static function dumpObject(object $object, string $label = ''): void
    {
        if (! self::isEnabled()) {
            return;
        }

        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
        $caller = $backtrace[0] ?? [];
        $reflection = new \ReflectionClass($object);

        $objectInfo = [
            'class' => $reflection->getName(),
            'methods' => array_map(fn ($method) => $method->getName(), $reflection->getMethods()),
            'properties' => array_map(fn ($prop) => $prop->getName(), $reflection->getProperties()),
            'data' => $object,
        ];

        $debugInfo = new DebugEntry(
            $objectInfo,
            $label ?: 'Object: ' . $reflection->getShortName(),
            $caller['file'] ?? 'unknown',
            $caller['line'] ?? 0,
            EntryType::OBJECT
        );

        self::getRegistry()->addEntry($debugInfo);
        self::getOutput()->output($debugInfo, self::getFormatter());
    }

    /**
     * Einfaches Logging für Debugging-Zwecke
     */
    public static function log(string $message, string $level = 'DEBUG'): void
    {
        if (! self::isEnabled()) {
            return;
        }

        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
        $caller = $backtrace[0] ?? [];

        $debugInfo = new DebugEntry(
            $message,
            $level,
            $caller['file'] ?? 'unknown',
            $caller['line'] ?? 0,
            EntryType::LOG
        );

        self::getRegistry()->addEntry($debugInfo);
        self::getOutput()->output($debugInfo, self::getFormatter());
    }

    /**
     * Zeigt eine Übersicht aller Debug-Aufrufe
     */
    public static function showHistory(): void
    {
        if (! self::isEnabled()) {
            return;
        }

        $entries = self::getRegistry()->getEntries();

        if (empty($entries)) {
            self::getOutput()->outputRaw("Keine Debug-Historie vorhanden.\n");

            return;
        }

        self::getOutput()->outputHistory($entries, self::getFormatter());
    }

    /**
     * Konfiguration des Debuggers
     */
    public static function configure(
        bool $enabled = true,
        ?OutputInterface $output = null,
        ?FormatterInterface $formatter = null
    ): void {
        self::$enabled = $enabled;

        if ($output !== null) {
            self::$output = $output;
        }

        if ($formatter !== null) {
            self::$formatter = $formatter;
        }
    }

    /**
     * Prüft, ob der Debugger aktiviert ist
     */
    public static function isEnabled(): bool
    {
        return self::$enabled;
    }

    /**
     * Deaktiviert den Debugger
     */
    public static function disable(): void
    {
        self::$enabled = false;
    }

    /**
     * Aktiviert den Debugger
     */
    public static function enable(): void
    {
        self::$enabled = true;
    }

    /**
     * Gibt das Output-Objekt zurück
     */
    private static function getOutput(): OutputInterface
    {
        if (self::$output === null) {
            $isConsole = PHP_SAPI === 'cli';
            self::$output = $isConsole
                ? new Outputs\ConsoleOutput()
                : new Outputs\HtmlOutput();
        }

        return self::$output;
    }

    /**
     * Gibt den Formatter zurück
     */
    private static function getFormatter(): FormatterInterface
    {
        if (self::$formatter === null) {
            $isConsole = PHP_SAPI === 'cli';
            self::$formatter = $isConsole
                ? new Formatters\ConsoleFormatter()
                : new Formatters\HtmlFormatter();
        }

        return self::$formatter;
    }

    /**
     * Gibt die Registry zurück
     */
    private static function getRegistry(): DebugRegistry
    {
        if (self::$registry === null) {
            self::$registry = new DebugRegistry();
        }

        return self::$registry;
    }
}
