<?php

declare(strict_types=1);

namespace App\Framework\ErrorHandling;

/**
 * Repräsentiert einen einzelnen Eintrag im Stacktrace
 */
final readonly class TraceItem
{
    public function __construct(
        public string $file,
        public int $line,
        public ?string $function,
        public ?string $class,
        public ?string $type,
        public array $args,
        public bool $isExceptionOrigin
    ) {
    }

    /**
     * Gibt den Dateipfad relativ zum Projekt-Root zurück
     */
    public function getRelativeFile(bool $namespace = true): string
    {
        $projectRoot = dirname(__DIR__, 3); // Annahme: 3 Verzeichnisse zurück zum Projekt-Root
        $name = str_replace($projectRoot, '', $this->file);

        return $namespace ? $name : $this->removeNamespace($name);
    }

    /**
     * Gibt den Klassennamen ohne Namespace zurück
     */
    public function getShortClass(): ?string
    {
        if (! $this->class) {
            return null;
        }

        $parts = explode('\\', $this->class);

        return end($parts);
    }

    /**
     * Gibt den Namespace zurück
     */
    public function getNamespace(): ?string
    {
        if (! $this->class) {
            return null;
        }

        $lastSlash = strrpos($this->class, '\\');

        return $lastSlash ? substr($this->class, 0, $lastSlash) : null;
    }

    /**
     * Gibt zurück, ob es sich um einen Methodenaufruf handelt
     */
    public function isMethod(): bool
    {
        return $this->class !== null && $this->function !== null;
    }

    /**
     * Gibt zurück, ob es sich um einen Funktionsaufruf handelt
     */
    public function isFunction(): bool
    {
        return $this->class === null && $this->function !== null;
    }

    /**
     * Gibt den Aufruf-String zurück (z.B. "Namespace\Class->method()")
     */
    public function getCallString(): string
    {
        if ($this->isExceptionOrigin) {
            return "Exception origin: >>" . $this->file . '<< in Line: ' . $this->line;
        }

        if ($this->isMethod()) {
            return $this->getShortClass() . $this->type . $this->function . '(' . $this->formatArgs() . ')';
        }

        if ($this->isFunction()) {
            return $this->function . '(' . $this->formatArgs() . ')';
        }

        return '';
    }

    /**
     * Formatiert die Argumente für die Anzeige
     */
    private function formatArgs(): string
    {
        if (empty($this->args)) {
            return '';
        }

        $formatted = [];

        foreach ($this->args as $arg) {
            $formatted[] = $this->formatArg($arg);
        }

        return implode(', ', $formatted);
    }

    /**
     * Formatiert ein einzelnes Argument für die Anzeige
     */
    private function formatArg(mixed $arg): string
    {
        return match(true) {
            is_string($arg) => '"' . (strlen($arg) > 20 ? substr($arg, 0, 20) . '...' : $arg) . '"',
            is_array($arg) => 'array(' . count($arg) . ')',
            is_object($arg) => $this->removeNamespace(get_class($arg)),
            is_bool($arg) => $arg ? 'true' : 'false',
            is_null($arg) => 'null',
            default => (string)$arg
        };
    }

    private function removeNamespace(string $class): string
    {
        $lastSlash = strrpos($class, '\\');

        return $lastSlash ? substr($class, $lastSlash + 1) : $class;
    }

    /**
     * String-Repräsentation dieses TraceItems
     */
    public function __toString(): string
    {
        $locationInfo = $this->file . ':' . $this->line;
        if ($this->isExceptionOrigin) {
            return "Exception origin: {$locationInfo}";
        }

        return $this->getCallString() . " at {$locationInfo}";
    }
}
