<?php

declare(strict_types=1);

namespace App\Framework\Logging\ValueObjects;

/**
 * User-Kontext Value Object für Structured Logging.
 *
 * Sammelt Benutzer-bezogene Informationen für Audit-Logs und
 * Benutzer-spezifische Log-Analyse.
 */
final readonly class UserContext
{
    public function __construct(
        public ?string $userId = null,
        public ?string $username = null,
        public ?string $email = null,
        public ?string $sessionId = null,
        public array $roles = [],
        public array $permissions = [],
        public ?string $authMethod = null,
        public bool $isAuthenticated = false,
        public array $metadata = []
    ) {
    }

    /**
     * Erstellt UserContext für authentifizierten Benutzer
     */
    public static function authenticated(
        string $userId,
        ?string $username = null,
        ?string $email = null,
        ?string $sessionId = null,
        array $roles = [],
        array $permissions = [],
        string $authMethod = 'session'
    ): self {
        return new self(
            userId: $userId,
            username: $username,
            email: $email,
            sessionId: $sessionId,
            roles: $roles,
            permissions: $permissions,
            authMethod: $authMethod,
            isAuthenticated: true
        );
    }

    /**
     * Erstellt UserContext für anonymen Benutzer
     */
    public static function anonymous(?string $sessionId = null): self
    {
        return new self(
            sessionId: $sessionId,
            isAuthenticated: false
        );
    }

    /**
     * Erstellt UserContext aus Session-Daten
     */
    public static function fromSession(array $sessionData): self
    {
        $userId = self::extractUserId($sessionData);
        $isAuthenticated = $userId !== null;

        return new self(
            userId: $userId,
            username: $sessionData['username'] ?? $sessionData['name'] ?? null,
            email: $sessionData['email'] ?? null,
            sessionId: $sessionData['session_id'] ?? null,
            roles: $sessionData['roles'] ?? [],
            permissions: $sessionData['permissions'] ?? [],
            authMethod: $sessionData['auth_method'] ?? 'session',
            isAuthenticated: $isAuthenticated,
            metadata: array_diff_key($sessionData, array_flip([
                'user_id', 'id', 'username', 'name', 'email', 'session_id',
                'roles', 'permissions', 'auth_method',
            ]))
        );
    }

    /**
     * Fügt Rolle hinzu
     */
    public function withRole(string $role): self
    {
        return new self(
            userId: $this->userId,
            username: $this->username,
            email: $this->email,
            sessionId: $this->sessionId,
            roles: array_unique([...$this->roles, $role]),
            permissions: $this->permissions,
            authMethod: $this->authMethod,
            isAuthenticated: $this->isAuthenticated,
            metadata: $this->metadata
        );
    }

    /**
     * Fügt Permission hinzu
     */
    public function withPermission(string $permission): self
    {
        return new self(
            userId: $this->userId,
            username: $this->username,
            email: $this->email,
            sessionId: $this->sessionId,
            roles: $this->roles,
            permissions: array_unique([...$this->permissions, $permission]),
            authMethod: $this->authMethod,
            isAuthenticated: $this->isAuthenticated,
            metadata: $this->metadata
        );
    }

    /**
     * Fügt Metadaten hinzu
     */
    public function withMetadata(string $key, mixed $value): self
    {
        return new self(
            userId: $this->userId,
            username: $this->username,
            email: $this->email,
            sessionId: $this->sessionId,
            roles: $this->roles,
            permissions: $this->permissions,
            authMethod: $this->authMethod,
            isAuthenticated: $this->isAuthenticated,
            metadata: array_merge($this->metadata, [$key => $value])
        );
    }

    /**
     * Prüft, ob Benutzer bestimmte Rolle hat
     */
    public function hasRole(string $role): bool
    {
        return in_array($role, $this->roles, true);
    }

    /**
     * Prüft, ob Benutzer bestimmte Permission hat
     */
    public function hasPermission(string $permission): bool
    {
        return in_array($permission, $this->permissions, true);
    }

    /**
     * Gibt anonymisierten User-Identifier zurück (für Privacy)
     */
    public function getAnonymizedId(): ?string
    {
        if ($this->userId === null) {
            return null;
        }

        // Hash für anonyme Logs
        return substr(hash('sha256', $this->userId), 0, 8);
    }

    /**
     * Maskiert Email für Logs
     */
    public function getMaskedEmail(): ?string
    {
        if ($this->email === null) {
            return null;
        }

        $parts = explode('@', $this->email);
        if (count($parts) !== 2) {
            return '***@***';
        }

        $local = $parts[0];
        $domain = $parts[1];

        // Maskiere lokalen Teil
        $maskedLocal = strlen($local) > 2
            ? substr($local, 0, 1) . str_repeat('*', strlen($local) - 2) . substr($local, -1)
            : str_repeat('*', strlen($local));

        return $maskedLocal . '@' . $domain;
    }

    /**
     * Konvertiert zu Array für Serialisierung
     */
    public function toArray(): array
    {
        $result = [
            'user_id' => $this->userId,
            'is_authenticated' => $this->isAuthenticated,
        ];

        if ($this->username !== null) {
            $result['username'] = $this->username;
        }

        if ($this->email !== null) {
            $result['email_masked'] = $this->getMaskedEmail();
        }

        if ($this->sessionId !== null) {
            $result['session_id'] = $this->sessionId;
        }

        if (! empty($this->roles)) {
            $result['roles'] = $this->roles;
        }

        if (! empty($this->permissions)) {
            $result['permissions'] = $this->permissions;
        }

        if ($this->authMethod !== null) {
            $result['auth_method'] = $this->authMethod;
        }

        if (! empty($this->metadata)) {
            $result['metadata'] = $this->metadata;
        }

        return $result;
    }

    /**
     * Konvertiert zu Array für Privacy-sichere Logs
     */
    public function toPrivacySafeArray(): array
    {
        return [
            'user_id_anonymized' => $this->getAnonymizedId(),
            'is_authenticated' => $this->isAuthenticated,
            'roles_count' => count($this->roles),
            'permissions_count' => count($this->permissions),
            'auth_method' => $this->authMethod,
            'has_session' => $this->sessionId !== null,
        ];
    }

    /**
     * Extrahiert User-ID aus verschiedenen Session-Formaten
     */
    private static function extractUserId(array $sessionData): ?string
    {
        $userIdKeys = ['user_id', 'id', 'user', 'auth_user_id', 'logged_in_user'];

        foreach ($userIdKeys as $key) {
            if (! isset($sessionData[$key])) {
                continue;
            }

            $userId = $sessionData[$key];

            // Handle user objects
            if (is_object($userId) && isset($userId->id)) {
                return (string) $userId->id;
            }

            // Handle arrays
            if (is_array($userId) && isset($userId['id'])) {
                return (string) $userId['id'];
            }

            // Handle simple values
            if (is_scalar($userId)) {
                return (string) $userId;
            }
        }

        return null;
    }
}
