<?php

declare(strict_types=1);

namespace TekDek\Employees\Tests;

use PHPUnit\Framework\TestCase;
use TekDek\Employees\EmployeeController;
use TekDek\Employees\EmployeeRepository;

/**
 * Tests for EmployeeController.
 *
 * Uses mocked repository to test controller logic in isolation.
 */
class EmployeeControllerTest extends TestCase
{
    private function createMockRepo(array $allData = [], ?array $singleData = null): EmployeeRepository
    {
        $mock = $this->createMock(EmployeeRepository::class);
        $mock->method('getAll')->willReturn($allData);
        $mock->method('getBySlug')->willReturnCallback(
            fn(string $slug) => $singleData[$slug] ?? null
        );
        return $mock;
    }

    private function captureOutput(callable $fn): string
    {
        ob_start();
        $fn();
        return ob_get_clean();
    }

    // --- Index Tests ---

    public function testIndexReturnsSuccessWithEmployees(): void
    {
        $employees = [
            ['slug' => 'daedalus', 'name' => 'Daedalus'],
            ['slug' => 'talos', 'name' => 'Talos'],
        ];
        $repo = $this->createMockRepo($employees);
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->index());
        $json = json_decode($output, true);

        $this->assertTrue($json['success']);
        $this->assertCount(2, $json['data']);
        $this->assertSame(2, $json['count']);
    }

    public function testIndexReturnsEmptyArray(): void
    {
        $repo = $this->createMockRepo([]);
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->index());
        $json = json_decode($output, true);

        $this->assertTrue($json['success']);
        $this->assertSame([], $json['data']);
        $this->assertSame(0, $json['count']);
    }

    // --- Show Tests ---

    public function testShowReturnsEmployee(): void
    {
        $employee = ['slug' => 'talos', 'name' => 'Talos', 'role' => 'Backend Engineering'];
        $repo = $this->createMockRepo([], ['talos' => $employee]);
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->show('talos'));
        $json = json_decode($output, true);

        $this->assertTrue($json['success']);
        $this->assertSame('Talos', $json['data']['name']);
    }

    public function testShowReturns404ForMissing(): void
    {
        $repo = $this->createMockRepo([], []);
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->show('nonexistent'));
        $json = json_decode($output, true);

        $this->assertFalse($json['success']);
        $this->assertStringContainsString('not found', $json['error']);
    }

    public function testShowRejects400ForInvalidSlug(): void
    {
        $repo = $this->createMockRepo();
        $controller = new EmployeeController($repo);

        // Slug with uppercase
        $output = $this->captureOutput(fn() => $controller->show('INVALID'));
        $json = json_decode($output, true);
        $this->assertFalse($json['success']);
        $this->assertStringContainsString('Invalid slug', $json['error']);
    }

    public function testShowRejectsSlugWithSpecialChars(): void
    {
        $repo = $this->createMockRepo();
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->show('sql;injection'));
        $json = json_decode($output, true);
        $this->assertFalse($json['success']);
    }

    public function testShowRejectsEmptySlug(): void
    {
        $repo = $this->createMockRepo();
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->show(''));
        $json = json_decode($output, true);
        $this->assertFalse($json['success']);
    }

    public function testShowAcceptsValidSlugFormats(): void
    {
        $employee = ['slug' => 'test-user-1', 'name' => 'Test'];
        $repo = $this->createMockRepo([], ['test-user-1' => $employee]);
        $controller = new EmployeeController($repo);

        $output = $this->captureOutput(fn() => $controller->show('test-user-1'));
        $json = json_decode($output, true);
        $this->assertTrue($json['success']);
    }
}
