<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Unit;

use OCA\Mitgliederverwaltung\Middleware\InputSanitizer;
use PHPUnit\Framework\TestCase;

class InputSanitizerTest extends TestCase {

    // -- sanitizeString --

    public function testSanitizeStringStripsHtmlTags(): void {
        $this->assertSame('Hello World', InputSanitizer::sanitizeString('<b>Hello</b> World'));
    }

    public function testSanitizeStringStripsPhpTags(): void {
        $this->assertSame('Hello', InputSanitizer::sanitizeString('<?php echo "evil"; ?>Hello'));
    }

    public function testSanitizeStringTrimsWhitespace(): void {
        $this->assertSame('Hello', InputSanitizer::sanitizeString('  Hello  '));
    }

    public function testSanitizeStringReturnsNullForNull(): void {
        $this->assertNull(InputSanitizer::sanitizeString(null));
    }

    public function testSanitizeStringPreservesSafeContent(): void {
        $this->assertSame('Normal text 123', InputSanitizer::sanitizeString('Normal text 123'));
    }

    public function testSanitizeStringPreservesUmlauts(): void {
        $this->assertSame('Woelflinge Maennlich', InputSanitizer::sanitizeString('Woelflinge Maennlich'));
    }

    public function testSanitizeStringHandlesEmptyString(): void {
        $this->assertSame('', InputSanitizer::sanitizeString(''));
    }

    public function testSanitizeStringHandlesNestedHtmlTags(): void {
        $this->assertSame('content', InputSanitizer::sanitizeString('<div><span><b>content</b></span></div>'));
    }

    public function testSanitizeStringHandlesMultipleScriptTags(): void {
        // strip_tags removes tags but preserves inner content
        $this->assertSame('before alert("x") after',
            InputSanitizer::sanitizeString('before <script>alert("x")</script> after'));
    }

    // -- sanitizeInt --

    public function testSanitizeIntReturnsIntForValidInt(): void {
        $this->assertSame(42, InputSanitizer::sanitizeInt(42));
    }

    public function testSanitizeIntReturnsIntForValidStringInt(): void {
        $this->assertSame(42, InputSanitizer::sanitizeInt('42'));
    }

    public function testSanitizeIntReturnsNullForNull(): void {
        $this->assertNull(InputSanitizer::sanitizeInt(null));
    }

    public function testSanitizeIntReturnsNullForEmptyString(): void {
        $this->assertNull(InputSanitizer::sanitizeInt(''));
    }

    public function testSanitizeIntReturnsNullForNonNumericString(): void {
        $this->assertNull(InputSanitizer::sanitizeInt('abc'));
    }

    public function testSanitizeIntReturnsNullForFloat(): void {
        $this->assertNull(InputSanitizer::sanitizeInt('3.14'));
    }

    public function testSanitizeIntHandlesNegative(): void {
        $this->assertSame(-5, InputSanitizer::sanitizeInt('-5'));
    }

    public function testSanitizeIntHandlesZero(): void {
        $this->assertSame(0, InputSanitizer::sanitizeInt(0));
    }

    // -- sanitizeDate --

    public function testSanitizeDateReturnsValidDate(): void {
        $this->assertSame('2026-04-09', InputSanitizer::sanitizeDate('2026-04-09'));
    }

    public function testSanitizeDateReturnsNullForNull(): void {
        $this->assertNull(InputSanitizer::sanitizeDate(null));
    }

    public function testSanitizeDateReturnsNullForEmptyString(): void {
        $this->assertNull(InputSanitizer::sanitizeDate(''));
    }

    public function testSanitizeDateReturnsNullForInvalidFormat(): void {
        $this->assertNull(InputSanitizer::sanitizeDate('09.04.2026'));
    }

    public function testSanitizeDateReturnsNullForInvalidDate(): void {
        $this->assertNull(InputSanitizer::sanitizeDate('2026-13-45'));
    }

    public function testSanitizeDateReturnsNullForFeb30(): void {
        $this->assertNull(InputSanitizer::sanitizeDate('2026-02-30'));
    }

    public function testSanitizeDateTrimsWhitespace(): void {
        $this->assertSame('2026-04-09', InputSanitizer::sanitizeDate('  2026-04-09  '));
    }

    public function testSanitizeDateReturnsNullForPartialDate(): void {
        $this->assertNull(InputSanitizer::sanitizeDate('2026-04'));
    }

    public function testSanitizeDateRejectsDateWithExtraChars(): void {
        $this->assertNull(InputSanitizer::sanitizeDate('2026-04-09T12:00'));
    }

    // -- sanitizeEmail --

    public function testSanitizeEmailReturnsValidEmail(): void {
        $this->assertSame('test@example.com', InputSanitizer::sanitizeEmail('test@example.com'));
    }

    public function testSanitizeEmailReturnsNullForNull(): void {
        $this->assertNull(InputSanitizer::sanitizeEmail(null));
    }

    public function testSanitizeEmailReturnsNullForEmptyString(): void {
        $this->assertNull(InputSanitizer::sanitizeEmail(''));
    }

    public function testSanitizeEmailReturnsNullForInvalidEmail(): void {
        $this->assertNull(InputSanitizer::sanitizeEmail('not-an-email'));
    }

    public function testSanitizeEmailReturnsNullForMissingDomain(): void {
        $this->assertNull(InputSanitizer::sanitizeEmail('user@'));
    }

    public function testSanitizeEmailTrimsWhitespace(): void {
        $this->assertSame('test@example.com', InputSanitizer::sanitizeEmail('  test@example.com  '));
    }

    // -- sanitizeArray --

    public function testSanitizeArraySanitizesStringFields(): void {
        $data = ['name' => '<b>John</b>'];
        $types = ['name' => 'string'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame('John', $result['name']);
    }

    public function testSanitizeArraySanitizesIntFields(): void {
        $data = ['age' => '25'];
        $types = ['age' => 'int'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame(25, $result['age']);
    }

    public function testSanitizeArraySanitizesDateFields(): void {
        $data = ['birthdate' => '2000-01-15'];
        $types = ['birthdate' => 'date'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame('2000-01-15', $result['birthdate']);
    }

    public function testSanitizeArraySanitizesEmailFields(): void {
        $data = ['email' => 'test@example.com'];
        $types = ['email' => 'email'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame('test@example.com', $result['email']);
    }

    public function testSanitizeArraySanitizesTextField(): void {
        $data = ['notes' => '<p>Some notes</p>'];
        $types = ['notes' => 'text'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame('Some notes', $result['notes']);
    }

    public function testSanitizeArrayHandlesBoolField(): void {
        $data = ['active' => 1];
        $types = ['active' => 'bool'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertTrue($result['active']);
    }

    public function testSanitizeArrayHandlesBoolFieldFalse(): void {
        $data = ['active' => 0];
        $types = ['active' => 'bool'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertFalse($result['active']);
    }

    public function testSanitizeArrayHandlesArrayField(): void {
        $data = ['items' => [1, 2, 3]];
        $types = ['items' => 'array'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame([1, 2, 3], $result['items']);
    }

    public function testSanitizeArrayHandlesArrayFieldWithNonArray(): void {
        $data = ['items' => 'not-an-array'];
        $types = ['items' => 'array'];
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame([], $result['items']);
    }

    public function testSanitizeArrayDefaultsToStringSanitization(): void {
        $data = ['unknown' => '<b>test</b>'];
        $types = []; // no type specified
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame('test', $result['unknown']);
    }

    public function testSanitizeArrayHandlesNonStringDefaultType(): void {
        $data = ['num' => 42];
        $types = []; // no type specified, but value is not a string
        $result = InputSanitizer::sanitizeArray($data, $types);
        $this->assertSame(42, $result['num']);
    }

    public function testSanitizeArrayHandlesMultipleFields(): void {
        $data = [
            'name' => '<script>alert("xss")</script>John',
            'age' => '30',
            'email' => 'john@example.com',
            'birthdate' => '1993-05-20',
        ];
        $types = [
            'name' => 'string',
            'age' => 'int',
            'email' => 'email',
            'birthdate' => 'date',
        ];
        $result = InputSanitizer::sanitizeArray($data, $types);

        $this->assertSame('alert("xss")John', $result['name']);
        $this->assertSame(30, $result['age']);
        $this->assertSame('john@example.com', $result['email']);
        $this->assertSame('1993-05-20', $result['birthdate']);
    }
}
