<?php

namespace App\Services\Upload;

use App\Services\Upload\Contracts\UploadDraftServiceInterface;
use Illuminate\Contracts\Filesystem\Filesystem as FilesystemContract;
use Illuminate\Filesystem\FilesystemManager;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class UploadDraftService implements UploadDraftServiceInterface
{
    protected FilesystemManager $filesystem;
    protected FilesystemContract $disk;
    protected string $diskName;
    protected string $basePath = 'tmp/drafts';

    public function __construct(FilesystemManager $filesystem, string $diskName = 'local')
    {
        $this->filesystem = $filesystem;
        $this->diskName = $diskName;
        $this->disk = $this->filesystem->disk($this->diskName);
    }

    public function createDraft(array $attributes = []): array
    {
        $id = (string) Str::uuid();
        $path = trim($this->basePath, '/') . '/' . $id;

        if (! $this->disk->exists($path)) {
            $this->disk->makeDirectory($path);
        }

        $meta = array_merge(['id' => $id, 'created_at' => Carbon::now()->toISOString()], $attributes);

        DB::table('uploads')->insert([
            'id' => $id,
            'user_id' => (int) ($attributes['user_id'] ?? 0),
            'type' => (string) ($attributes['type'] ?? 'image'),
            'status' => 'draft',
            'moderation_status' => 'pending',
            'processing_state' => 'pending_scan',
            'expires_at' => null,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $this->writeMeta($id, $meta);

        return ['id' => $id, 'path' => $path, 'meta' => $meta];
    }

    public function storeMainFile(string $draftId, UploadedFile $file): array
    {
        $dir = trim($this->basePath, '/') . '/' . $draftId . '/main';
        if (! $this->disk->exists($dir)) {
            $this->disk->makeDirectory($dir);
        }

        $filename = time() . '_' . preg_replace('/[^A-Za-z0-9_\.-]/', '_', $file->getClientOriginalName());
        $storedPath = $this->disk->putFileAs($dir, $file, $filename);

        $size = $this->safeSize($storedPath, $file);
        $mime = $file->getClientMimeType() ?? $this->safeMimeType($storedPath);
        $hash = $this->calculateHash($file->getRealPath() ?: $storedPath);

        $info = ['path' => $storedPath, 'size' => $size, 'mime' => $mime, 'hash' => $hash];

        $meta = $this->readMeta($draftId);
        $meta['main_file'] = $info;
        $this->writeMeta($draftId, $meta);

        DB::table('upload_files')->insert([
            'upload_id' => $draftId,
            'path' => $storedPath,
            'type' => 'main',
            'hash' => $hash,
            'size' => $size,
            'mime' => $mime,
            'created_at' => now(),
        ]);

        return $info;
    }

    public function storeScreenshot(string $draftId, UploadedFile $file): array
    {
        $dir = trim($this->basePath, '/') . '/' . $draftId . '/screenshots';
        if (! $this->disk->exists($dir)) {
            $this->disk->makeDirectory($dir);
        }

        $filename = time() . '_' . preg_replace('/[^A-Za-z0-9_\.-]/', '_', $file->getClientOriginalName());
        $storedPath = $this->disk->putFileAs($dir, $file, $filename);

        $size = $this->safeSize($storedPath, $file);
        $mime = $file->getClientMimeType() ?? $this->safeMimeType($storedPath);
        $hash = $this->calculateHash($file->getRealPath() ?: $storedPath);

        $info = ['path' => $storedPath, 'size' => $size, 'mime' => $mime, 'hash' => $hash];

        $meta = $this->readMeta($draftId);
        $meta['screenshots'][] = $info;
        $this->writeMeta($draftId, $meta);

        DB::table('upload_files')->insert([
            'upload_id' => $draftId,
            'path' => $storedPath,
            'type' => 'screenshot',
            'hash' => $hash,
            'size' => $size,
            'mime' => $mime,
            'created_at' => now(),
        ]);

        return $info;
    }

    public function calculateHash(string $filePath): string
    {
        // If path points to a local filesystem file
        if (is_file($filePath)) {
            return hash_file('sha256', $filePath);
        }

        // If path is a storage-relative path
        if ($this->disk->exists($filePath)) {
            $contents = $this->disk->get($filePath);
            return hash('sha256', $contents);
        }

        throw new \RuntimeException('File not found for hashing: ' . $filePath);
    }

    public function setExpiration(string $draftId, ?Carbon $expiresAt = null): bool
    {
        $meta = $this->readMeta($draftId);
        $meta['expires_at'] = $expiresAt?->toISOString();
        $this->writeMeta($draftId, $meta);

        DB::table('uploads')->where('id', $draftId)->update([
            'expires_at' => $expiresAt,
            'updated_at' => now(),
        ]);

        return true;
    }

    protected function metaPath(string $draftId): string
    {
        return trim($this->basePath, '/') . '/' . $draftId . '/meta.json';
    }

    protected function readMeta(string $draftId): array
    {
        $path = $this->metaPath($draftId);
        if (! $this->disk->exists($path)) {
            return [];
        }

        $raw = $this->disk->get($path);
        $decoded = json_decode($raw, true);
        return is_array($decoded) ? $decoded : [];
    }

    protected function writeMeta(string $draftId, array $meta): void
    {
        $path = $this->metaPath($draftId);
        $this->disk->put($path, json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
    }

    protected function safeSize(string $storedPath, UploadedFile $file): int
    {
        try {
            return $this->disk->size($storedPath);
        } catch (\Throwable $e) {
            return (int) $file->getSize();
        }
    }

    protected function safeMimeType(string $storedPath): ?string
    {
        try {
            return $this->disk->mimeType($storedPath);
        } catch (\Throwable $e) {
            return null;
        }
    }
}
