<?php

declare(strict_types=1);

namespace App\Application\Security\ValueObjects;

final class MaskedEmail
{
    private function __construct(
        private string $maskedValue,
        private string $original
    ) {
    }

    public static function fromString(string $email): self
    {
        if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
            // Nicht-E-Mail-Strings nur minimal maskieren
            return new self(
                self::maskGenericString($email),
                $email
            );
        }

        return new self(
            self::maskEmailAddress($email),
            $email
        );
    }

    public static function fromStringWithStrategy(string $email, MaskingStrategy $strategy): self
    {
        if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return new self(
                self::maskGenericString($email),
                $email
            );
        }

        $maskedValue = match ($strategy) {
            MaskingStrategy::STANDARD => self::maskEmailAddress($email),
            MaskingStrategy::PARTIAL => self::maskEmailPartial($email),
            MaskingStrategy::DOMAIN_ONLY => self::maskDomainOnly($email),
            MaskingStrategy::HASH => self::hashEmail($email)
        };

        return new self($maskedValue, $email);
    }

    public function getMaskedValue(): string
    {
        return $this->maskedValue;
    }

    public function getOriginal(): string
    {
        return $this->original;
    }

    public function toString(): string
    {
        return $this->maskedValue;
    }

    public function __toString(): string
    {
        return $this->maskedValue;
    }

    private static function maskEmailAddress(string $email): string
    {
        [$local, $domain] = explode('@', $email, 2);

        if (strlen($local) <= 2) {
            return str_repeat('*', strlen($local)) . '@' . $domain;
        }

        $maskedLocal = substr($local, 0, 2) . str_repeat('*', strlen($local) - 2);

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

    private static function maskEmailPartial(string $email): string
    {
        [$local, $domain] = explode('@', $email, 2);

        if (strlen($local) <= 3) {
            return $local[0] . str_repeat('*', max(0, strlen($local) - 1)) . '@' . $domain;
        }

        $maskedLocal = $local[0] . str_repeat('*', strlen($local) - 2) . substr($local, -1);

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

    private static function maskDomainOnly(string $email): string
    {
        [$local, $domain] = explode('@', $email, 2);

        // Domain verschleiern, aber Local-Part zeigen
        $domainParts = explode('.', $domain);
        if (count($domainParts) > 1) {
            $tld = array_pop($domainParts);
            $maskedDomain = str_repeat('*', strlen(implode('.', $domainParts))) . '.' . $tld;
        } else {
            $maskedDomain = str_repeat('*', strlen($domain));
        }

        return $local . '@' . $maskedDomain;
    }

    private static function hashEmail(string $email): string
    {
        return 'user_' . substr(hash('sha256', $email), 0, 8);
    }

    private static function maskGenericString(string $input): string
    {
        if (strlen($input) <= 3) {
            return str_repeat('*', strlen($input));
        }

        return substr($input, 0, 2) . str_repeat('*', strlen($input) - 2);
    }
}
