<?php

namespace Tests\Unit\Storage;

use App\Services\Storage\FileStorageException;
use App\Services\Storage\LocalStorageDriver;
use App\Services\Storage\S3StorageDriver;
use CodeIgniter\HTTP\DownloadResponse;
use Config\Storage;
use Tests\Support\Storage\StorageTestCase;

/**
 * @internal
 */
final class StorageConfigTest extends StorageTestCase
{
    public function testDefaultsToLocalDriverWhenEnvMissing(): void
    {
        $config = new Storage();

        $this->assertSame('local', $config->driver);
    }

    public function testInvalidDriverFallsBackToLocal(): void
    {
        $previous = getenv('FILE_STORAGE_DRIVER');
        putenv('FILE_STORAGE_DRIVER=invalid');

        try {
            $config = new Storage();
            $this->assertSame('local', $config->driver);
        } finally {
            if ($previous === false) {
                putenv('FILE_STORAGE_DRIVER');
            } else {
                putenv('FILE_STORAGE_DRIVER=' . $previous);
            }
        }
    }

    public function testRetainLocalParsesBooleanEnv(): void
    {
        $previous = getenv('RETAIN_LOCAL');
        putenv('RETAIN_LOCAL=true');

        try {
            $config = new Storage();
            $this->assertTrue($config->retainLocal);
        } finally {
            if ($previous === false) {
                putenv('RETAIN_LOCAL');
            } else {
                putenv('RETAIN_LOCAL=' . $previous);
            }
        }
    }

    public function testS3DriverRequiresCredentials(): void
    {
        $config               = new Storage();
        $config->driver       = 's3';
        $config->s3Bucket     = '';
        $config->s3AccessKey  = '';
        $config->s3SecretKey  = '';

        $this->expectException(FileStorageException::class);
        $this->expectExceptionMessage('S3 storage is not configured');

        new S3StorageDriver($config);
    }

    public function testFileStorageServiceReportsDriverMode(): void
    {
        $this->assertFalse(storage()->isS3());

        $this->storageConfig->driver = 's3';
        $this->storageConfig->s3Bucket    = 'test-bucket';
        $this->storageConfig->s3AccessKey = 'key';
        $this->storageConfig->s3SecretKey = 'secret';

        \Config\Services::injectMock('fileStorage', new \App\Services\FileStorageService($this->storageConfig));

        $this->assertTrue(storage()->isS3());
    }
}

/**
 * @internal
 */
final class LocalStorageDriverTest extends StorageTestCase
{
    private LocalStorageDriver $driver;

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

        $this->driver = new LocalStorageDriver($this->storageConfig);
    }

    public function testUploadFromUploadedFileStoresUnderLocalRoot(): void
    {
        $file = $this->createUploadedFile('%PDF-1.4 policy', 'policy.pdf', 'application/pdf');
        $key  = 'uploads/policy/policy_pdf/test_policy.pdf';

        $this->driver->upload($file, $key);

        $this->assertFileExists($this->localFilePath($key));
        $this->assertSame('%PDF-1.4 policy', file_get_contents($this->localFilePath($key)));
    }

    public function testUploadFromLocalPathCopiesFile(): void
    {
        $source = tempnam(sys_get_temp_dir(), 'src_');
        file_put_contents($source, 'receipt bytes');
        $key = 'uploads/policy/policy_payment_receipt/receipt.pdf';

        $this->driver->upload($source, $key);

        $this->assertFileExists($this->localFilePath($key));
        $this->assertSame('receipt bytes', file_get_contents($this->localFilePath($key)));
    }

    public function testPutWriteRawContents(): void
    {
        $key = 'uploads/policy/policy_md/sample.md';

        $this->driver->put($key, '# Markdown content');

        $this->assertTrue($this->driver->exists($key));
        $this->assertSame('# Markdown content', $this->driver->read($key));
    }

    public function testExistsReturnsFalseForMissingFile(): void
    {
        $this->assertFalse($this->driver->exists('uploads/policy/policy_pdf/missing.pdf'));
    }

    public function testDeleteRemovesFile(): void
    {
        $key = 'uploads/endorsement/original.pdf';
        $this->driver->put($key, 'endorsement');

        $this->assertTrue($this->driver->delete($key));
        $this->assertFalse($this->driver->exists($key));
    }

    public function testDownloadToTempReturnsExistingLocalPath(): void
    {
        $key = 'uploads/policy/policy_pdf/read.pdf';
        $this->driver->put($key, 'pdf-data');

        $path = $this->driver->downloadToTemp($key);

        $this->assertSame($this->localFilePath($key), $path);
    }

    public function testCopyDuplicatesFile(): void
    {
        $from = 'uploads/policy/policy_pdf/source.pdf';
        $to   = 'uploads/policy/policy_pdf/copy.pdf';
        $this->driver->put($from, 'copy-me');

        $this->assertTrue($this->driver->copy($from, $to));
        $this->assertSame('copy-me', $this->driver->read($to));
    }

    public function testTemporaryUrlUsesBaseUrlForLocalDriver(): void
    {
        $key = 'uploads/policy/policy_pdf/url.pdf';
        $this->driver->put($key, 'url');

        $url = $this->driver->temporaryUrl($key);

        $this->assertStringContainsString('uploads/policy/policy_pdf/url.pdf', $url);
    }

    public function testStreamDownloadReturnsResponseWithBody(): void
    {
        $key = 'uploads/endorsement/endorsement_pdf/completion.pdf';
        $this->driver->put($key, 'completion pdf');

        $response = $this->driver->streamDownload($key, 'completion.pdf');

        $this->assertInstanceOf(DownloadResponse::class, $response);
        $this->assertDownloadHeaders($response, 'attachment');
        $this->assertSame(strlen('completion pdf'), $response->getContentLength());
    }
}
