<?php

namespace Tests\Unit\Uploads;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\UploadedFile;
use App\Services\Upload\UploadDraftService;
use Illuminate\Contracts\Filesystem\Filesystem as FilesystemContract;
use Illuminate\Filesystem\FilesystemManager;
use Carbon\Carbon;
use App\Models\User;

class UploadDraftServiceTest extends TestCase
{
    use RefreshDatabase;

    protected UploadDraftService $service;
    protected User $user;

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

        // Use fake storage so we don't touch the real filesystem
        Storage::fake('local');

        $this->user = User::factory()->create();

        // Provide a dummy clamav scanner binding so any scanning calls are mocked
        $this->app->instance('clamav', new class {
            public function scan(string $path): bool
            {
                return true;
            }
        });

        $filesystem = $this->app->make(FilesystemManager::class);
        $this->service = new UploadDraftService($filesystem, 'local');
    }

    public function test_createDraft_creates_directory_and_writes_meta()
    {
        $result = $this->service->createDraft(['title' => 'Test Draft', 'user_id' => $this->user->id, 'type' => 'image']);

        $this->assertArrayHasKey('id', $result);
        $id = $result['id'];

        Storage::disk('local')->assertExists("tmp/drafts/{$id}");
        Storage::disk('local')->assertExists("tmp/drafts/{$id}/meta.json");

        $meta = json_decode(Storage::disk('local')->get("tmp/drafts/{$id}/meta.json"), true);
        $this->assertSame('Test Draft', $meta['title']);
        $this->assertSame($id, $meta['id']);
    }

    public function test_storeMainFile_saves_file_and_updates_meta()
    {
        $draft = $this->service->createDraft(['user_id' => $this->user->id, 'type' => 'image']);
        $id = $draft['id'];

        $file = UploadedFile::fake()->create('song.mp3', 1500, 'audio/mpeg');

        $info = $this->service->storeMainFile($id, $file);

        $this->assertArrayHasKey('path', $info);
        Storage::disk('local')->assertExists($info['path']);

        $meta = json_decode(Storage::disk('local')->get("tmp/drafts/{$id}/meta.json"), true);
        $this->assertArrayHasKey('main_file', $meta);
        $this->assertSame($info['hash'], $meta['main_file']['hash']);
    }

    public function test_storeScreenshot_saves_file_and_appends_meta()
    {
        $draft = $this->service->createDraft(['user_id' => $this->user->id, 'type' => 'image']);
        $id = $draft['id'];

        $img = UploadedFile::fake()->image('thumb.jpg', 640, 480);

        $info = $this->service->storeScreenshot($id, $img);

        $this->assertArrayHasKey('path', $info);
        Storage::disk('local')->assertExists($info['path']);

        $meta = json_decode(Storage::disk('local')->get("tmp/drafts/{$id}/meta.json"), true);
        $this->assertArrayHasKey('screenshots', $meta);
        $this->assertCount(1, $meta['screenshots']);
        $this->assertSame($info['hash'], $meta['screenshots'][0]['hash']);
    }

    public function test_calculateHash_for_local_file_and_storage_path()
    {
        $file = UploadedFile::fake()->create('doc.pdf', 10);
        $realPath = $file->getRealPath();

        $expected = hash_file('sha256', $realPath);
        $this->assertSame($expected, $this->service->calculateHash($realPath));

        // Store into drafts and calculate by storage path
        $draft = $this->service->createDraft(['user_id' => $this->user->id, 'type' => 'image']);
        $id = $draft['id'];
        $info = $this->service->storeMainFile($id, $file);

        $storageHash = $this->service->calculateHash($info['path']);
        $storedContents = Storage::disk('local')->get($info['path']);
        $this->assertSame(hash('sha256', $storedContents), $storageHash);
    }

    public function test_setExpiration_writes_expires_at_in_meta()
    {
        $draft = $this->service->createDraft(['user_id' => $this->user->id, 'type' => 'image']);
        $id = $draft['id'];

        $when = Carbon::now()->addDays(3);
        $ok = $this->service->setExpiration($id, $when);
        $this->assertTrue($ok);

        $meta = json_decode(Storage::disk('local')->get("tmp/drafts/{$id}/meta.json"), true);
        $this->assertArrayHasKey('expires_at', $meta);
        $this->assertSame($when->toISOString(), $meta['expires_at']);
    }

    public function test_calculateHash_throws_for_missing_file()
    {
        $this->expectException(\RuntimeException::class);
        $this->service->calculateHash('this/path/does/not/exist');
    }
}
