<?php

declare(strict_types=1);

require_once __DIR__ . '/../../vendor/autoload.php';

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Attributes\DefaultImplementation;
use App\Framework\DI\DefaultContainer;
use App\Framework\DI\DefaultImplementationProcessor;
use App\Framework\DI\Exceptions\DefaultImplementationException;
use App\Framework\Discovery\Results\AttributeRegistry;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\ValueObjects\AttributeTarget;
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;

// Test Interfaces
interface UserRepository
{
    public function findById(string $id): ?object;
}

interface LoggerInterface
{
    public function log(string $message): void;
}

interface FormattableInterface
{
    public function format(): string;
}

// Test Classes

// Scenario 1: Explicit interface specification
#[DefaultImplementation(UserRepository::class)]
final readonly class DatabaseUserRepository implements UserRepository
{
    public function findById(string $id): ?object
    {
        return (object) ['id' => $id, 'name' => 'Test User'];
    }
}

// Scenario 2: Auto-detect single interface
#[DefaultImplementation]
final readonly class FileUserRepository implements UserRepository
{
    public function findById(string $id): ?object
    {
        return (object) ['id' => $id, 'name' => 'File User'];
    }
}

// Scenario 3: Auto-detect with multiple interfaces (should use first)
#[DefaultImplementation]
final readonly class AppLogger implements LoggerInterface, FormattableInterface
{
    public function log(string $message): void
    {
        echo "[LOG] $message\n";
    }

    public function format(): string
    {
        return 'AppLogger';
    }
}

// Scenario 4: Invalid - explicit interface not implemented (for error testing)
#[DefaultImplementation(LoggerInterface::class)]
final readonly class BadRepository implements UserRepository
{
    public function findById(string $id): ?object
    {
        return null;
    }
}

// Scenario 5: Invalid - no interfaces implemented (for error testing)
#[DefaultImplementation]
final readonly class NoInterfaceClass
{
    public function doSomething(): void {}
}

// Helper function to create DiscoveredAttribute
function createDiscoveredAttribute(string $className, array $attributeArgs = []): DiscoveredAttribute
{
    return new DiscoveredAttribute(
        className: ClassName::create($className),
        attributeClass: DefaultImplementation::class,
        target: AttributeTarget::TARGET_CLASS,
        arguments: $attributeArgs
    );
}

// Helper function to create DiscoveryRegistry with specific attributes
function createRegistryWithAttributes(array $discoveredAttributes): DiscoveryRegistry
{
    $attributeRegistry = new AttributeRegistry();
    foreach ($discoveredAttributes as $attr) {
        $attributeRegistry->add(DefaultImplementation::class, $attr);
    }

    return new DiscoveryRegistry(
        attributes: $attributeRegistry,
        interfaces: new \App\Framework\Discovery\Results\InterfaceRegistry(),
        templates: new \App\Framework\Discovery\Results\TemplateRegistry()
    );
}

// Test Runner
echo "=== DefaultImplementation Attribute Tests ===\n\n";

// Test 1: Explicit Interface Specification
echo "Test 1: Explicit interface specification\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered = createDiscoveredAttribute(DatabaseUserRepository::class, ['interface' => UserRepository::class]);
    $registry = createRegistryWithAttributes([$discovered]);

    $registered = $processor->process($registry);

    echo "✓ Registered $registered binding(s)\n";
    echo "✓ Container has binding: " . ($container->has(UserRepository::class) ? 'YES' : 'NO') . "\n";

    $instance = $container->get(UserRepository::class);
    echo "✓ Instance type: " . get_class($instance) . "\n";
    echo "✓ Find user: " . json_encode($instance->findById('123')) . "\n";
    echo "PASSED\n\n";
} catch (\Exception $e) {
    echo "✗ FAILED: " . $e->getMessage() . "\n\n";
}

// Test 2: Auto-detect Single Interface
echo "Test 2: Auto-detect single interface\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered = createDiscoveredAttribute(FileUserRepository::class, []); // No explicit interface
    $registry = createRegistryWithAttributes([$discovered]);

    $registered = $processor->process($registry);

    echo "✓ Registered $registered binding(s)\n";
    echo "✓ Container has binding: " . ($container->has(UserRepository::class) ? 'YES' : 'NO') . "\n";

    $instance = $container->get(UserRepository::class);
    echo "✓ Instance type: " . get_class($instance) . "\n";
    echo "PASSED\n\n";
} catch (\Exception $e) {
    echo "✗ FAILED: " . $e->getMessage() . "\n\n";
}

// Test 3: Auto-detect Multiple Interfaces (uses first)
echo "Test 3: Auto-detect with multiple interfaces\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered = createDiscoveredAttribute(AppLogger::class, []);
    $registry = createRegistryWithAttributes([$discovered]);

    $registered = $processor->process($registry);

    echo "✓ Registered $registered binding(s)\n";
    echo "✓ Container has LoggerInterface: " . ($container->has(LoggerInterface::class) ? 'YES' : 'NO') . "\n";

    $instance = $container->get(LoggerInterface::class);
    echo "✓ Instance type: " . get_class($instance) . "\n";
    $instance->log("Test message");
    echo "PASSED\n\n";
} catch (\Exception $e) {
    echo "✗ FAILED: " . $e->getMessage() . "\n\n";
}

// Test 4: Error - Explicit Interface Not Implemented
echo "Test 4: Error - explicit interface not implemented\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered = createDiscoveredAttribute(BadRepository::class, ['interface' => LoggerInterface::class]);
    $registry = createRegistryWithAttributes([$discovered]);

    $processor->process($registry);
    echo "✗ FAILED: Should have thrown exception\n\n";
} catch (DefaultImplementationException $e) {
    echo "✓ Caught expected exception: " . $e->getMessage() . "\n";
    echo "PASSED\n\n";
}

// Test 5: Error - No Interfaces Implemented
echo "Test 5: Error - no interfaces implemented\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered = createDiscoveredAttribute(NoInterfaceClass::class, []);
    $registry = createRegistryWithAttributes([$discovered]);

    $processor->process($registry);
    echo "✗ FAILED: Should have thrown exception\n\n";
} catch (DefaultImplementationException $e) {
    echo "✓ Caught expected exception: " . $e->getMessage() . "\n";
    echo "PASSED\n\n";
}

// Test 6: Multiple Bindings
echo "Test 6: Multiple bindings in one registry\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered1 = createDiscoveredAttribute(DatabaseUserRepository::class, ['interface' => UserRepository::class]);
    $discovered2 = createDiscoveredAttribute(AppLogger::class, []);

    $registry = createRegistryWithAttributes([$discovered1, $discovered2]);

    $registered = $processor->process($registry);

    echo "✓ Registered $registered binding(s)\n";
    echo "✓ Has UserRepository: " . ($container->has(UserRepository::class) ? 'YES' : 'NO') . "\n";
    echo "✓ Has LoggerInterface: " . ($container->has(LoggerInterface::class) ? 'YES' : 'NO') . "\n";
    echo "PASSED\n\n";
} catch (\Exception $e) {
    echo "✗ FAILED: " . $e->getMessage() . "\n\n";
}

// Test 7: Singleton Registration (verify it's actually singleton)
echo "Test 7: Singleton registration verification\n";
try {
    $container = new DefaultContainer();
    $processor = new DefaultImplementationProcessor($container);

    $discovered = createDiscoveredAttribute(DatabaseUserRepository::class, ['interface' => UserRepository::class]);
    $registry = createRegistryWithAttributes([$discovered]);

    $processor->process($registry);

    $instance1 = $container->get(UserRepository::class);
    $instance2 = $container->get(UserRepository::class);

    echo "✓ Instance 1 ID: " . spl_object_id($instance1) . "\n";
    echo "✓ Instance 2 ID: " . spl_object_id($instance2) . "\n";
    echo "✓ Same instance: " . ($instance1 === $instance2 ? 'YES' : 'NO') . "\n";
    echo "PASSED\n\n";
} catch (\Exception $e) {
    echo "✗ FAILED: " . $e->getMessage() . "\n\n";
}

echo "=== All Tests Complete ===\n";
