<?php

namespace App\Helpers\FirstParty\ImageHash;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Jenssegers\ImageHash\ImageHash;
use Jenssegers\ImageHash\Implementations\DifferenceHash;

class ImageHashService
{
    private ImageHash $hasher;

    public function __construct()
    {
        $this->hasher = new ImageHash(new DifferenceHash);
    }

    public function generateHashFromUrl(string $url): ?string
    {
        try {
            $response = Http::timeout(30)->get($url);

            if (! $response->successful()) {
                Log::warning("Failed to download image from URL: {$url}");

                return null;
            }

            $imageData = $response->body();

            return $this->generateHashFromData($imageData);

        } catch (\Exception $e) {
            Log::error("Error generating hash from URL {$url}: ".$e->getMessage());

            return null;
        }
    }

    public function generateHashFromData(string $imageData): ?string
    {
        try {
            $tempFile = tempnam(sys_get_temp_dir(), 'imagehash_');
            file_put_contents($tempFile, $imageData);

            $hash = $this->hasher->hash($tempFile);
            unlink($tempFile);

            return $hash->toHex();

        } catch (\Exception $e) {
            Log::error('Error generating hash from image data: '.$e->getMessage());

            return null;
        }
    }

    public function calculateHammingDistance(string $hash1, string $hash2): int
    {
        // Validate hashes are not empty
        if (empty($hash1) || empty($hash2)) {
            return PHP_INT_MAX; // Return max distance for invalid hashes
        }

        // Pad shorter hash with zeros to make them equal length
        $maxLength = max(strlen($hash1), strlen($hash2));
        $hash1 = str_pad($hash1, $maxLength, '0', STR_PAD_LEFT);
        $hash2 = str_pad($hash2, $maxLength, '0', STR_PAD_LEFT);

        $distance = 0;
        for ($i = 0; $i < $maxLength; $i++) {
            if ($hash1[$i] !== $hash2[$i]) {
                $distance++;
            }
        }

        return $distance;
    }

    public function areHashesSimilar(string $hash1, string $hash2, int $threshold = 5): bool
    {
        return $this->calculateHammingDistance($hash1, $hash2) <= $threshold;
    }

    public function findSimilarHashes(string $targetHash, array $hashes, int $threshold = 5): array
    {
        $similar = [];

        foreach ($hashes as $id => $hash) {
            if ($this->areHashesSimilar($targetHash, $hash, $threshold)) {
                $similar[$id] = $this->calculateHammingDistance($targetHash, $hash);
            }
        }

        asort($similar);

        return $similar;
    }
}
