<?php

namespace Tests\Unit;

use App\Services\SegmindFaceSwapService;
use App\UserFacePhoto;
use App\VirtualTryonResult;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Tests\RefreshDatabaseWithoutSeeds;
use Tests\TestCase;

class SegmindFaceSwapServiceTest extends TestCase
{
    use RefreshDatabaseWithoutSeeds;

    protected $service;

    protected function setUp(): void
    {
        parent::setUp();

        if (!extension_loaded('gd')) {
            $this->markTestSkipped('GD extension required for Segmind service tests');
        }

        $this->service = new SegmindFaceSwapService();
        // In testing env, the service uses the 'tryon_local' disk.
        Storage::fake('tryon_local');
        Storage::fake('product_images');
        // Force "real API" code path for these tests; Http::fake() intercepts the call.
        config(['faceswap.segmind_api_key' => 'test-key']);
    }

    protected function createTestResult(): VirtualTryonResult
    {
        $user = \App\User::create([
            'name' => 'Test',
            'email' => 'test-segmind@example.com',
            'password' => bcrypt('password'),
        ]);

        $product = \App\Product::create([
            'name' => 'Test Product',
            'code' => 'SEGMIND-TEST',
            'slug' => 'segmind-test',
            'image' => 'segmind-test.jpg',
            'type' => 1,
            'status' => 1,
            'is_activated' => 1,
            'is_deleted' => 0,
        ]);

        $photo = UserFacePhoto::create([
            'user_id' => $user->id,
            'file_path' => 'https://cdn.example.com/face.jpg',
            'storage_path' => 'face-photos/' . $user->id . '/face.jpg',
            'is_default' => true,
        ]);

        // Put fake face photo in the (testing) private disk
        Storage::disk('tryon_local')->put($photo->storage_path, $this->createMinimalJpeg());

        // Put fake product image in the product_images disk
        Storage::disk('product_images')->put('segmind-test.jpg', $this->createMinimalJpeg());

        // `code` and `user_id` are excluded from $fillable; assign directly.
        $result = new VirtualTryonResult();
        $result->code = 'segmind-test-' . rand(1000, 9999);
        $result->user_id = $user->id;
        $result->fill([
            'product_id' => $product->id,
            'face_photo_id' => $photo->id,
            'source_image' => 'segmind-test.jpg',
            'status' => 'pending',
            'expires_at' => now()->addDays(90),
        ]);
        $result->save();

        return $result;
    }

    protected function createMinimalJpeg(): string
    {
        // Generate a real 100x100 JPEG via GD so Intervention can decode it.
        $img = imagecreatetruecolor(100, 100);
        imagefilledrectangle($img, 0, 0, 100, 100, imagecolorallocate($img, 200, 200, 200));
        ob_start();
        imagejpeg($img, null, 85);
        $data = ob_get_clean();
        imagedestroy($img);
        return $data;
    }

    protected function tearDown(): void
    {
        parent::tearDown();
    }

    public function test_swap_success_updates_status_to_completed()
    {
        $result = $this->createTestResult();

        // Mock Segmind API returning a JPEG image
        Http::fake([
            'api.segmind.com/*' => Http::response($this->createMinimalJpeg(), 200, [
                'Content-Type' => 'image/jpeg',
            ]),
        ]);

        $this->service->swap($result);

        $result->refresh();
        $this->assertEquals('completed', $result->status);
        $this->assertNotNull($result->result_image);
        Storage::disk('tryon_local')->assertExists($result->result_image);
    }

    public function test_swap_failure_updates_status_to_failed()
    {
        $result = $this->createTestResult();

        Http::fake([
            'api.segmind.com/*' => Http::response('Server Error', 500),
        ]);

        try {
            $this->service->swap($result);
        } catch (\Exception $e) {
            // Expected
        }

        $result->refresh();
        $this->assertEquals('failed', $result->status);
        $this->assertNotNull($result->error);
    }

    public function test_swap_timeout_updates_status_to_failed()
    {
        $result = $this->createTestResult();

        Http::fake([
            'api.segmind.com/*' => function () {
                throw new \Illuminate\Http\Client\ConnectionException('Connection timed out');
            },
        ]);

        try {
            $this->service->swap($result);
        } catch (\Exception $e) {
            // Expected
        }

        $result->refresh();
        $this->assertEquals('failed', $result->status);
    }

    public function test_swap_handles_base64_json_response()
    {
        $result = $this->createTestResult();
        $imageData = $this->createMinimalJpeg();

        Http::fake([
            'api.segmind.com/*' => Http::response(json_encode([
                'image' => base64_encode($imageData),
            ]), 200, [
                'Content-Type' => 'application/json',
            ]),
        ]);

        $this->service->swap($result);

        $result->refresh();
        $this->assertEquals('completed', $result->status);
        Storage::disk('tryon_local')->assertExists($result->result_image);
    }

    public function test_swap_fails_when_face_photo_missing()
    {
        $result = $this->createTestResult();

        // Delete the face photo from storage
        Storage::disk('tryon_local')->delete($result->facePhoto->storage_path);

        try {
            $this->service->swap($result);
            $this->fail('Expected exception was not thrown');
        } catch (\Exception $e) {
            // Laravel's Storage::get() on a missing file throws "File not found at path: ...".
            $this->assertMatchesRegularExpression('/(Failed to read face photo|File not found)/', $e->getMessage());
        }

        $result->refresh();
        $this->assertEquals('failed', $result->status);
    }
}
