<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Models\File as MediaFile;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
// NOTE: Storage facade instantiates FinfoMimeTypeDetector which crashes on
// PHP builds without fileinfo (this server runs PHP --disable-fileinfo).
// We avoid `Storage::disk()` entirely and operate on raw paths under storage_path().
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class FileController extends Controller
{
    private const MAX_BYTES = 50 * 1024 * 1024; // 50 MB

    private const ALLOWED_MIMES = [
        'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
        'application/pdf',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'application/vnd.ms-powerpoint',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'text/plain', 'text/csv', 'text/markdown',
        'application/zip', 'application/x-zip-compressed',
        'application/json',
        'video/mp4', 'video/webm',
        'audio/mpeg', 'audio/mp4', 'audio/wav',
    ];

    public function index(Request $request): JsonResponse
    {
        $q = MediaFile::query()->with('uploader:id,name,username');

        if ($mime = $request->query('mime')) {
            $q->where('mime', 'like', $mime.'%');
        }
        if ($folder = $request->query('folder')) {
            $q->where('folder', $folder);
        }
        if ($search = $request->query('q')) {
            $q->where('name', 'like', "%{$search}%");
        }

        $files = $q->orderByDesc('created_at')->paginate((int) $request->query('per_page', 30));

        return response()->json(['data' => $files]);
    }

    public function store(Request $request): JsonResponse
    {
        $this->authorizePerm($request, 'files.upload');

        $request->validate([
            'file' => ['required', 'file', 'max:'.(self::MAX_BYTES / 1024)],
            'folder' => ['nullable', 'string', 'max:191'],
        ]);

        $uploaded = $request->file('file');
        // Prefer client mime when fileinfo extension is unavailable.
        try {
            $mime = $uploaded->getMimeType() ?: $uploaded->getClientMimeType();
        } catch (\Throwable $e) {
            $mime = $uploaded->getClientMimeType();
        }
        $mime = $mime ?: 'application/octet-stream';

        if (! in_array($mime, self::ALLOWED_MIMES, true)) {
            return response()->json([
                'error' => ['code' => 'MIME_NOT_ALLOWED', 'mime' => $mime],
            ], 422);
        }

        $tenantId = app('currentTenant')->getKey();
        $folder = $request->input('folder', '/') ?: '/';
        $ym = now()->format('Y-m');
        $relDir = "tenants/{$tenantId}/files/{$ym}";
        $ext = $uploaded->getClientOriginalExtension();
        $storedName = Str::uuid()->toString().($ext ? '.'.$ext : '');

        // Avoid Storage facade which constructs FinfoMimeTypeDetector.
        $diskRoot = storage_path('app/public');
        $absDir = rtrim($diskRoot, '/').'/'.$relDir;
        if (! is_dir($absDir)) {
            @mkdir($absDir, 0775, true);
        }
        $sourcePath = $uploaded->getRealPath();
        $destAbs = $absDir.'/'.$storedName;
        if (! @copy($sourcePath, $destAbs)) {
            return response()->json(['error' => ['code' => 'UPLOAD_COPY_FAILED']], 500);
        }
        @chmod($destAbs, 0664);
        $path = $relDir.'/'.$storedName;

        $hash = hash_file('sha256', $destAbs);

        $file = MediaFile::create([
            'tenant_id' => $tenantId,
            'user_id' => $request->user()?->id,
            'folder' => $folder,
            'name' => $uploaded->getClientOriginalName(),
            'path' => $path,
            'mime' => $mime,
            'size' => $uploaded->getSize() ?: 0,
            'hash' => $hash,
            'meta' => [
                'ext' => $ext,
                'original' => $uploaded->getClientOriginalName(),
            ],
        ]);

        return response()->json(['data' => $file], 201);
    }

    public function show(string $id): JsonResponse
    {
        $file = MediaFile::with('uploader:id,name,username')->findOrFail($id);

        return response()->json(['data' => $file]);
    }

    public function destroy(Request $request, string $id): JsonResponse
    {
        $this->authorizePerm($request, 'files.delete');

        $file = MediaFile::findOrFail($id);

        if ($file->path) {
            $abs = storage_path('app/public/'.$file->path);
            if (is_file($abs)) {
                @unlink($abs);
            }
        }
        $file->delete();

        return response()->json(['data' => ['id' => $id, 'deleted' => true]]);
    }

    public function rename(Request $request, string $id): JsonResponse
    {
        $this->authorizePerm($request, 'files.upload');

        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'folder' => ['nullable', 'string', 'max:191'],
        ]);

        $file = MediaFile::findOrFail($id);
        $file->name = $data['name'];
        if (! empty($data['folder'])) {
            $file->folder = $data['folder'];
        }
        $file->save();

        return response()->json(['data' => $file->fresh()]);
    }

    private function authorizePerm(Request $request, string $perm): void
    {
        $user = $request->user();
        if (! $user) {
            throw new AccessDeniedHttpException('UNAUTH');
        }
        if ($user->hasRole('super_admin') || $user->hasRole('tenant_admin')) {
            return;
        }
        if (! $user->can($perm)) {
            throw new AccessDeniedHttpException("PERM_DENIED:{$perm}");
        }
    }
}
