<?php

declare(strict_types=1);

namespace App\Tests\Functional\Controller;

use App\Entity\Device;
use App\Entity\Image;
use App\Enum\DeviceModel;
use App\Enum\Orientation;
use App\Tests\Functional\AppWebTestCase;

class DeviceApiControllerTest extends AppWebTestCase
{
    private function makeDevice(string $mac, $user): Device
    {
        $device = new Device();
        $device->setMac($mac);
        $device->setName('Frame ' . $mac);
        $device->setUser($user);
        $device->setModel(DeviceModel::V1);
        $device->setOrientation(Orientation::Landscape);
        $this->em()->persist($device);
        $this->em()->flush();
        return $device;
    }

    private function makeImage($user): Image
    {
        $image = (new Image())->setUser($user)->setOriginalFilename('x.jpg')->setStoragePath('x');
        $this->em()->persist($image);
        $this->em()->flush();
        return $image;
    }

    public function test_list_returns_own_devices(): void
    {
        $user   = $this->createUser('list@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A1', $user);
        $client = $this->loginAs($user);

        $client->request('GET', '/api/devices');

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertCount(1, $data);
        $this->assertSame($device->getMac(), $data[0]['mac']);
    }

    public function test_list_unauthenticated_returns_redirect(): void
    {
        $this->client->request('GET', '/api/devices');

        // form_login firewall redirects unauthenticated requests to /login
        $this->assertResponseRedirects('/login');
    }

    public function test_patch_updates_name_orientation_and_interval(): void
    {
        $user   = $this->createUser('patch@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A2', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode([
            'name'                    => 'New Name',
            'orientation'             => 'portrait',
            'rotationIntervalMinutes' => 30,
        ]));

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame('New Name', $data['name']);
        $this->assertSame('portrait', $data['orientation']);
        $this->assertSame(30, $data['rotationIntervalMinutes']);
    }

    public function test_patch_wrong_users_device_returns_404(): void
    {
        $owner  = $this->createUser('owner@example.com');
        $other  = $this->createUser('other@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A3', $owner);
        $client = $this->loginAs($other);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['name' => 'Hack']));

        $this->assertResponseStatusCodeSame(404);
    }

    public function test_put_lock_sets_locked_image_id(): void
    {
        $user   = $this->createUser('lock@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A4', $user);
        $image  = $this->makeImage($user);
        $image->approveForDevice($device);
        $this->em()->flush();
        $client = $this->loginAs($user);

        $client->request('PUT', '/api/devices/' . $device->getId() . '/lock', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['imageId' => $image->getId()]));

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame($image->getId(), $data['lockedImageId']);
    }

    // A-06: PUT /lock with image not approved for the device → 422
    public function test_put_lock_unapproved_image_returns_422(): void
    {
        $user   = $this->createUser('lock422@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A8', $user);
        $image  = $this->makeImage($user);
        // Image is owned by user but NOT approved for this device
        $client = $this->loginAs($user);

        $client->request('PUT', '/api/devices/' . $device->getId() . '/lock', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['imageId' => $image->getId()]));

        $this->assertResponseStatusCodeSame(422);
    }

    public function test_put_lock_image_not_owned_by_user_returns_404(): void
    {
        $user1  = $this->createUser('lockown1@example.com');
        $user2  = $this->createUser('lockown2@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A5', $user1);
        $image  = $this->makeImage($user2);
        $client = $this->loginAs($user1);

        $client->request('PUT', '/api/devices/' . $device->getId() . '/lock', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['imageId' => $image->getId()]));

        $this->assertResponseStatusCodeSame(404);
    }

    public function test_delete_lock_clears_locked_image_id(): void
    {
        $user   = $this->createUser('unlock@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A6', $user);
        $image  = $this->makeImage($user);
        $device->setLockedImage($image);
        $this->em()->flush();
        $client = $this->loginAs($user);

        $client->request('DELETE', '/api/devices/' . $device->getId() . '/lock');

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertNull($data['lockedImageId']);
    }

    public function test_put_lock_wrong_users_device_returns_404(): void
    {
        $owner  = $this->createUser('lockwrong1@example.com');
        $other  = $this->createUser('lockwrong2@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:A7', $owner);
        $image  = $this->makeImage($other);
        $client = $this->loginAs($other);

        $client->request('PUT', '/api/devices/' . $device->getId() . '/lock', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['imageId' => $image->getId()]));

        $this->assertResponseStatusCodeSame(404);
    }

    public function test_patch_empty_name_returns_422(): void
    {
        $user   = $this->createUser('patchname@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:B1', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['name' => '']));

        $this->assertResponseStatusCodeSame(422);
    }

    public function test_patch_invalid_orientation_returns_422(): void
    {
        $user   = $this->createUser('patchorient@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:B2', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['orientation' => 'diagonal']));

        $this->assertResponseStatusCodeSame(422);
    }

    public function test_patch_invalid_timezone_returns_422(): void
    {
        $user   = $this->createUser('patchtzone@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:B3', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['timezone' => 'Not/Real']));

        $this->assertResponseStatusCodeSame(422);
    }

    public function test_patch_sets_wake_hour(): void
    {
        $user   = $this->createUser('patchwake@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:B4', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['wakeHour' => 8]));

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(8, $data['wakeHour']);
    }

    public function test_patch_sets_uniqueness_window(): void
    {
        $user   = $this->createUser('patchuniq@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:B5', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['uniquenessWindow' => 14]));

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(14, $data['uniquenessWindow']);
    }

    public function test_patch_sets_valid_timezone(): void
    {
        $user   = $this->createUser('patchtzvld@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:C1', $user);
        $client = $this->loginAs($user);

        $client->request('PATCH', '/api/devices/' . $device->getId(), [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['timezone' => 'America/New_York']));

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame('America/New_York', $data['timezone']);
    }

    public function test_lock_with_no_image_id_returns_422(): void
    {
        $user   = $this->createUser('locknoimgid@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:C2', $user);
        $client = $this->loginAs($user);

        $client->request('PUT', '/api/devices/' . $device->getId() . '/lock', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode([]));

        $this->assertResponseStatusCodeSame(422);
    }

    public function test_unlock_wrong_users_device_returns_404(): void
    {
        $owner  = $this->createUser('unlockown@example.com');
        $other  = $this->createUser('unlockoth@example.com');
        $device = $this->makeDevice('AA:BB:CC:DD:EE:C3', $owner);
        $client = $this->loginAs($other);

        $client->request('DELETE', '/api/devices/' . $device->getId() . '/lock');

        $this->assertResponseStatusCodeSame(404);
    }
}
