<?php

namespace App\Services\Files;

use RuntimeException;

class VirusScanService
{
    protected ?string $driver;

    public function __construct()
    {
        $this->driver = config('file_upload.virus_scan.driver', 'null');
    }

    /**
     * Check if virus scanning is available.
     */
    public function isAvailable(): bool
    {
        if ($this->driver === 'null') {
            return false;
        }

        if ($this->driver === 'clamav') {
            return $this->isClamAvAvailable();
        }

        return false;
    }

    /**
     * Scan a file for viruses.
     *
     * @return string 'clean', 'infected', or 'error'
     */
    public function scan(string $filePath): string
    {
        if ($this->driver === 'clamav') {
            return $this->scanWithClamAv($filePath);
        }

        return 'skipped';
    }

    /**
     * Check if ClamAV is available.
     */
    protected function isClamAvAvailable(): bool
    {
        $socket = config('file_upload.virus_scan.clamav.socket');
        $host = config('file_upload.virus_scan.clamav.host');
        $port = config('file_upload.virus_scan.clamav.port', 3310);

        // Try socket connection first
        if ($socket && file_exists($socket)) {
            return true;
        }

        // Try TCP connection
        if ($host) {
            $connection = @fsockopen($host, $port, $errno, $errstr, 5);

            if ($connection) {
                fclose($connection);

                return true;
            }
        }

        return false;
    }

    /**
     * Scan a file using ClamAV.
     */
    protected function scanWithClamAv(string $filePath): string
    {
        $socket = config('file_upload.virus_scan.clamav.socket');
        $host = config('file_upload.virus_scan.clamav.host');
        $port = config('file_upload.virus_scan.clamav.port', 3310);
        $timeout = config('file_upload.virus_scan.clamav.timeout', 30);

        try {
            // Connect to ClamAV
            if ($socket && file_exists($socket)) {
                $socketConnection = fsockopen('unix://'.$socket, -1, $errno, $errstr, $timeout);
            } else {
                $socketConnection = fsockopen($host, $port, $errno, $errstr, $timeout);
            }

            if (! $socketConnection) {
                throw new RuntimeException("Cannot connect to ClamAV: {$errstr}");
            }

            // Send SCAN command
            fwrite($socketConnection, "SCAN {$filePath}\n");

            // Read response
            $response = fgets($socketConnection);
            fclose($socketConnection);

            // Parse response
            // Response format: /path/to/file: OK
            // or: /path/to/file: Trojan.Generic FOUND
            if (strpos($response, ' FOUND') !== false) {
                return 'infected';
            }

            if (strpos($response, ' OK') !== false) {
                return 'clean';
            }

            return 'error';
        } catch (\Exception $e) {
            throw new RuntimeException("ClamAV scan failed: {$e->getMessage()}");
        }
    }

    /**
     * Scan content directly (for chunks or in-memory data).
     */
    public function scanContent(string $content): string
    {
        // Write to temp file
        $tempPath = tempnam(sys_get_temp_dir(), 'scan_');
        file_put_contents($tempPath, $content);

        try {
            return $this->scan($tempPath);
        } finally {
            unlink($tempPath);
        }
    }
}
