<?php

namespace App\Services\Security;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

class PhotoSecurityScanner
{
    public function scan(string $disk, ?string $relativePath): array
    {
        if (! $relativePath) {
            return [
                'status' => 'error',
                'message' => 'Missing path for antivirus scan.',
            ];
        }

        if (! config('security.antivirus.enabled', false)) {
            return [
                'status' => 'skipped',
                'message' => 'Antivirus scanning disabled.',
            ];
        }

        [$absolutePath, $message] = $this->resolveAbsolutePath($disk, $relativePath);

        if (! $absolutePath) {
            return [
                'status' => 'skipped',
                'message' => $message ?? 'Unable to resolve path for antivirus.',
            ];
        }

        $binary = config('security.antivirus.binary', '/usr/bin/clamscan');
        $arguments = config('security.antivirus.arguments', '--no-summary');
        $timeout = config('security.antivirus.timeout', 60);

        $command = $binary.' '.$arguments.' '.escapeshellarg($absolutePath);

        try {
            $process = Process::fromShellCommandline($command);
            $process->setTimeout($timeout);
            $process->run();

            if ($process->isSuccessful()) {
                return [
                    'status' => 'clean',
                    'message' => trim($process->getOutput()),
                ];
            }

            if ($process->getExitCode() === 1) {
                return [
                    'status' => 'infected',
                    'message' => trim($process->getOutput() ?: $process->getErrorOutput()),
                ];
            }

            return [
                'status' => 'error',
                'message' => trim($process->getErrorOutput() ?: 'Unknown antivirus error.'),
            ];
        } catch (ProcessFailedException $exception) {
            return [
                'status' => 'error',
                'message' => $exception->getMessage(),
            ];
        } catch (\Throwable $exception) {
            Log::warning('[PhotoSecurity] Antivirus scan failed', [
                'disk' => $disk,
                'path' => $relativePath,
                'error' => $exception->getMessage(),
            ]);

            return [
                'status' => 'error',
                'message' => $exception->getMessage(),
            ];
        }
    }

    public function stripExif(string $disk, ?string $relativePath): array
    {
        if (! config('security.exif.strip', true)) {
            return [
                'status' => 'skipped',
                'message' => 'EXIF stripping disabled.',
            ];
        }

        if (! $relativePath) {
            return [
                'status' => 'error',
                'message' => 'Missing path for EXIF stripping.',
            ];
        }

        [$absolutePath, $message] = $this->resolveAbsolutePath($disk, $relativePath);

        if (! $absolutePath) {
            return [
                'status' => 'skipped',
                'message' => $message ?? 'Unable to resolve path for EXIF stripping.',
            ];
        }

        $extension = strtolower(pathinfo($absolutePath, PATHINFO_EXTENSION));
        if (! in_array($extension, ['jpg', 'jpeg', 'png', 'webp'], true)) {
            return [
                'status' => 'skipped',
                'message' => 'Unsupported format for EXIF stripping.',
            ];
        }

        try {
            $contents = @file_get_contents($absolutePath);
            if ($contents === false) {
                throw new \RuntimeException('Unable to read file for EXIF stripping.');
            }

            $image = @imagecreatefromstring($contents);
            if (! $image) {
                return [
                    'status' => 'skipped',
                    'message' => 'Unable to decode image for EXIF stripping.',
                ];
            }

            $result = match ($extension) {
                'png' => imagepng($image, $absolutePath),
                'webp' => function_exists('imagewebp') ? imagewebp($image, $absolutePath) : false,
                default => imagejpeg($image, $absolutePath, 90),
            };

            imagedestroy($image);

            if (! $result) {
                return [
                    'status' => 'error',
                    'message' => 'Failed to re-encode image without EXIF.',
                ];
            }

            return [
                'status' => 'stripped',
                'message' => 'EXIF metadata removed via re-encode.',
            ];
        } catch (\Throwable $exception) {
            Log::warning('[PhotoSecurity] EXIF stripping failed', [
                'disk' => $disk,
                'path' => $relativePath,
                'error' => $exception->getMessage(),
            ]);

            return [
                'status' => 'error',
                'message' => $exception->getMessage(),
            ];
        }
    }

    /**
     * @return array{0: string|null, 1: string|null}
     */
    private function resolveAbsolutePath(string $disk, string $relativePath): array
    {
        try {
            $storage = Storage::disk($disk);

            if (! method_exists($storage, 'path')) {
                return [null, 'Storage driver does not expose local paths.'];
            }

            $absolute = $storage->path($relativePath);

            if (! file_exists($absolute)) {
                return [null, 'File not found on disk.'];
            }

            return [$absolute, null];
        } catch (\Throwable $exception) {
            Log::warning('[PhotoSecurity] Unable to resolve absolute path', [
                'disk' => $disk,
                'path' => $relativePath,
                'error' => $exception->getMessage(),
            ]);

            return [null, $exception->getMessage()];
        }
    }
}

