<?php

namespace App\Http\Controllers;

use App\Models\NoticeGallary;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;

class NoticeGallaryController extends Controller
{
    /**
     * Get all NoticeGallary records.
     */
    public function index()
    {
        $galleries = NoticeGallary::with(['noticeBoard'])->get();
        $galleries->each(function ($gallery) {
            $images = json_decode($gallery->Image, true) ?? [];
            $gallery->Image = array_map(function ($imagePath) {
                return url('storage/' . $imagePath);
            }, $images);
        });
        return response()->json($galleries);
    }

    /**
     * Get a specific NoticeGallary record by Ntg_Id.
     */
    public function show($id)
    {
        $gallery = NoticeGallary::with(['noticeBoard'])->find($id);
        if (!$gallery) {
            return response()->json(['message' => 'Record not found'], 404);
        }
        $images = json_decode($gallery->Image, true) ?? [];
        $gallery->Image = array_map(function ($imagePath) {
            return url('storage/' . $imagePath);
        }, $images);
        return response()->json($gallery);
    }

    /**
     * Create a new NoticeGallary record with multiple image, PDF, and document uploads.
     */
    public function store(Request $request)
    {
        Log::debug('Store request data:', $request->all());
        $validator = Validator::make($request->all(), [
            'Ntc_Id'    => 'required|integer|exists:notice_board,Ntc_Id',
            'Files'     => 'nullable|array',
            'Files.*'   => 'file|mimes:jpeg,png,jpg,gif,svg,pdf,doc,docx|max:20480',
            'Img_Date'  => 'nullable|date_format:d/m/Y H:i:s',
            'Img_Status' => 'nullable|string|max:1',
        ], [
            'Files.*.mimes' => 'Each file must be a valid image (jpeg, png, jpg, gif, svg), PDF, or document (doc, docx).',
            'Files.*.max' => 'Each file must not exceed 20MB.'
        ]);

        if ($validator->fails()) {
            Log::error('Validation failed:', $validator->errors()->toArray());
            return response()->json(['errors' => $validator->errors()], 422);
        }

        try {
            $imgDate = $request->input('Img_Date') ? Carbon::createFromFormat('d/m/Y H:i:s', $request->input('Img_Date'))->format('Y-m-d H:i:s') : null;
        } catch (\Exception $e) {
            Log::error('Invalid date format:', ['error' => $e->getMessage()]);
            return response()->json(['error' => 'Invalid date format. Use dd/mm/yyyy H:i:s'], 422);
        }

        $basePath = public_path('storage');
        $filePaths = [];
        if ($request->hasFile('Files')) {
            foreach ($request->file('Files') as $file) {
                $originalName = $file->getClientOriginalName();
                $extension = $file->getClientOriginalExtension();
                $filePath = 'notice_gallary/' . $originalName;
                $destinationPath = $basePath . '/' . $filePath;

                if (file_exists($destinationPath)) {
                    $filenameWithoutExt = pathinfo($originalName, PATHINFO_FILENAME);
                    $newFileName = $filenameWithoutExt . '_' . time() . '.' . $extension;
                    $filePath = 'notice_gallary/' . $newFileName;
                    $destinationPath = $basePath . '/' . $filePath;
                }

                if (!file_exists(dirname($destinationPath))) {
                    mkdir(dirname($destinationPath), 0755, true);
                }

                $file->move(dirname($destinationPath), basename($filePath));
                $filePaths[] = $filePath;
            }
        }

        $gallery = NoticeGallary::create([
            'Ntc_Id'    => $request->input('Ntc_Id'),
            'Image'     => json_encode($filePaths),
            'Img_Date'  => $imgDate,
            'Img_Status' => $request->input('Img_Status'),
        ]);

        $responseData = $gallery->toArray();
        $responseData['Image'] = array_map(function ($path) {
            return url('/storage/' . $path);
        }, json_decode($gallery->Image, true) ?? []);

        Log::info('Gallery created:', $responseData);
        return response()->json($responseData, 201);
    }

    /**
     * Update a NoticeGallary record by Ntg_Id.
     */

    public function update(Request $request, $id)
    {
        Log::debug('Update request data:', $request->all());

        $validator = Validator::make($request->all(), [
            'Ntc_Id'    => 'required|integer|exists:notice_board,Ntc_Id',
            'Image'     => 'nullable|array',
            'Image.*'   => [
                'nullable',
                'string',
                function ($attribute, $value, $fail) {
                    // Check if it's a URL (existing file)
                    if (preg_match('/^https?:\/\/.*\/storage\/notice_gallary\/(.+\.(pdf|doc|docx|jpg|jpeg|png|gif|svg))$/i', $value)) {
                        Log::debug("Valid URL detected for $attribute", ['value' => substr($value, 0, 100)]);
                        return; // Valid URL
                    }

                    // Check if it's a Base64 string with data: prefix
                    if (preg_match('/^data:(image\/(jpeg|png|jpg|gif|svg)|application\/(pdf|msword|vnd\.openxmlformats-officedocument\.wordprocessingml\.document));base64,(.+)/', $value, $matches)) {
                        $base64Data = $matches[3];
                        $mimeType = $matches[1];
                        $decodedData = base64_decode($base64Data, true);
                        if ($decodedData === false) {
                            Log::error("Base64 decoding failed for $attribute", ['mimeType' => $mimeType]);
                            $fail("The $attribute contains invalid Base64 data.");
                            return;
                        }
                        if (strlen($decodedData) > 20480 * 1024) { // 20MB in bytes
                            Log::error("File size too large for $attribute", ['mimeType' => $mimeType, 'size' => strlen($decodedData)]);
                            $fail("The $attribute file size must not exceed 20480 KB.");
                        }
                        Log::debug("Valid Base64 data with data: prefix for $attribute", ['mimeType' => $mimeType, 'size' => strlen($decodedData)]);
                        return;
                    }

                    // Check if it's a raw Base64 string
                    if (preg_match('/^[A-Za-z0-9+\/=]+$/', $value)) {
                        $decodedData = base64_decode($value, true);
                        if ($decodedData === false) {
                            Log::error("Base64 decoding failed for raw Base64 $attribute");
                            $fail("The $attribute contains invalid Base64 data.");
                            return;
                        }
                        if (strlen($decodedData) > 20480 * 1024) { // 20MB in bytes
                            Log::error("File size too large for raw Base64 $attribute", ['size' => strlen($decodedData)]);
                            $fail("The $attribute file size must not exceed 20480 KB.");
                        }
                        Log::debug("Valid raw Base64 data for $attribute", ['size' => strlen($decodedData)]);
                        return;
                    }

                    Log::error("Invalid format for $attribute", ['value' => substr($value, 0, 100)]);
                    $fail("The $attribute must be a valid Base64 string of type: pdf, doc, docx, jpg, jpeg, png, gif, svg, or a valid file URL.");
                },
            ],
            'Img_Date'  => 'nullable|date_format:d/m/Y H:i:s',
            'Img_Status' => 'nullable|string|max:1',
        ], [
            'Image.*.mimes' => 'Each file must be a valid image (jpeg, png, jpg, gif, svg), PDF, or document (doc, docx).',
            'Image.*.max' => 'Each file must not exceed 20MB.'
        ]);

        if ($validator->fails()) {
            Log::error('Validation failed:', $validator->errors()->toArray());
            return response()->json(['errors' => $validator->errors()], 422);
        }

        try {
            $imgDate = $request->input('Img_Date') ? Carbon::createFromFormat('d/m/Y H:i:s', $request->input('Img_Date'))->format('Y-m-d H:i:s') : null;
        } catch (\Exception $e) {
            Log::error('Invalid date format:', ['error' => $e->getMessage()]);
            return response()->json(['error' => 'Invalid date format. Use dd/mm/yyyy H:i:s'], 422);
        }

        $gallery = NoticeGallary::findOrFail($id);

        $basePath = public_path('storage');
        $filePaths = json_decode($gallery->Image, true) ?? [];

        if ($request->has('Image') && is_array($request->input('Image'))) {
            foreach ($request->input('Image') as $index => $item) {
                // Handle URLs (existing files)
                if (preg_match('/^https?:\/\/.*\/storage\/notice_gallary\/(.+\.(pdf|doc|docx|jpg|jpeg|png|gif|svg))$/i', $item, $matches)) {
                    $relativePath = 'notice_gallary/' . $matches[1];
                    $filePaths[$index] = $relativePath; // Place at specific index
                    Log::info("Preserved existing file URL for Image[{$index}]", ['path' => $relativePath]);
                    continue;
                }

                // Handle Base64 strings (with or without data: prefix)
                $base64Data = $item;
                $extension = null;
                $mimeType = null;

                // Check if it has data: prefix
                if (preg_match('/^data:(image\/(jpeg|png|jpg|gif|svg)|application\/(pdf|msword|vnd\.openxmlformats-officedocument\.wordprocessingml\.document));base64,(.+)/', $item, $matches)) {
                    $mimeType = $matches[1];
                    $base64Data = $matches[3];
                    $extension = $this->getExtensionFromMimeType($mimeType);
                } else {
                    // For raw Base64, attempt to detect file type from decoded data
                    $decodedData = base64_decode($item, true);
                    if ($decodedData === false) {
                        Log::error("Base64 decoding failed for Image[{$index}]", ['item' => substr($item, 0, 100)]);
                        return response()->json(['error' => "Invalid Base64 data for Image[{$index}]"], 422);
                    }
                    // Attempt to detect file type using file signatures
                    $extension = $this->detectFileExtension($decodedData);
                    $mimeType = $this->getMimeTypeFromExtension($extension);
                    $base64Data = $item; // Use original Base64 string
                }

                // Decode Base64 string
                $fileData = base64_decode($base64Data, true);
                if ($fileData === false) {
                    Log::error("Base64 decoding failed for Image[{$index}]", ['mimeType' => $mimeType]);
                    return response()->json(['error' => "Invalid Base64 data for Image[{$index}]"], 422);
                }

                // Delete old file at this index if it exists
                if (isset($filePaths[$index])) {
                    $oldFilePath = $filePaths[$index];
                    $fullOldPath = public_path('storage/' . $oldFilePath);
                    if (file_exists($fullOldPath)) {
                        unlink($fullOldPath);
                        Log::info("Deleted old file for Image[{$index}]", ['path' => $oldFilePath]);
                    }
                }

                // Generate a unique filename
                $fileName = time() . '-' . uniqid() . '.' . $extension;
                $filePath = 'notice_gallary/' . $fileName;
                $destinationPath = $basePath . '/' . $filePath;

                // Ensure the directory exists
                if (!file_exists(dirname($destinationPath))) {
                    mkdir(dirname($destinationPath), 0755, true);
                }

                // Save the file
                $bytesWritten = file_put_contents($destinationPath, $fileData);
                if ($bytesWritten === false) {
                    Log::error("Failed to save file for Image[{$index}]", ['path' => $filePath, 'mimeType' => $mimeType]);
                    return response()->json(['error' => "Failed to save file for Image[{$index}]"], 500);
                }
                $filePaths[$index] = $filePath; // Replace at specific index
                Log::info("Saved file for Image[{$index}]", ['path' => $filePath, 'mimeType' => $mimeType, 'size' => strlen($fileData)]);
            }
        }

        $data = [
            'Ntc_Id'    => $request->input('Ntc_Id'),
            'Image'     => !empty($filePaths) ? json_encode(array_values($filePaths)) : null,
            'Img_Date'  => $imgDate,
            'Img_Status' => $request->input('Img_Status'),
        ];

        $gallery->update($data);

        $responseData = $gallery->toArray();
        $responseData['Image'] = $gallery->Image ? array_map(function ($path) {
            return url('storage/' . $path);
        }, json_decode($gallery->Image, true)) : null;

        Log::info('Gallery updated:', $responseData);
        return response()->json($responseData, 200);
    }

    /**
     * Get file extension from MIME type
     *
     * @param string $mimeType
     * @return string
     */
    private function getExtensionFromMimeType($mimeType)
    {
        $mimeMap = [
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/gif' => 'gif',
            'image/svg+xml' => 'svg',
            'application/pdf' => 'pdf',
            'application/msword' => 'doc',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
        ];

        $extension = $mimeMap[$mimeType] ?? 'bin';
        Log::debug("Mapped MIME type to extension", ['mimeType' => $mimeType, 'extension' => $extension]);
        return $extension;
    }

    /**
     * Detect file extension from decoded data
     *
     * @param string $data
     * @return string
     */
    private function detectFileExtension($data)
    {
        // Check file signatures (magic numbers)
        if (strncmp($data, '%PDF-', 5) === 0) {
            Log::debug("Detected PDF file signature");
            return 'pdf';
        }
        if (strncmp($data, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) === 0) {
            Log::debug("Detected DOC file signature");
            return 'doc';
        }
        // DOCX is a ZIP file with specific XML content, check for ZIP signature
        if (strncmp($data, "\x50\x4B\x03\x04", 4) === 0) {
            Log::debug("Detected DOCX file signature (ZIP)");
            return 'docx';
        }
        // Image signatures
        if (strncmp($data, "\xFF\xD8\xFF", 3) === 0) {
            Log::debug("Detected JPEG file signature");
            return 'jpg';
        }
        if (strncmp($data, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) === 0) {
            Log::debug("Detected PNG file signature");
            return 'png';
        }
        if (strncmp($data, "GIF89a", 6) === 0 || strncmp($data, "GIF87a", 6) === 0) {
            Log::debug("Detected GIF file signature");
            return 'gif';
        }
        if (strpos($data, '<svg') !== false) {
            Log::debug("Detected SVG file signature");
            return 'svg';
        }

        Log::warning("Could not detect file type, defaulting to bin");
        return 'bin';
    }

    /**
     * Get MIME type from extension
     *
     * @param string $extension
     * @return string
     */
    private function getMimeTypeFromExtension($extension)
    {
        $extensionMap = [
            'jpg' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'svg' => 'image/svg+xml',
            'pdf' => 'application/pdf',
            'doc' => 'application/msword',
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        ];

        $mimeType = $extensionMap[strtolower($extension)] ?? 'application/octet-stream';
        Log::debug("Mapped extension to MIME type", ['extension' => $extension, 'mimeType' => $mimeType]);
        return $mimeType;
    }

    /**
     * Delete specific files from a NoticeGallary record by Ntg_Id.
     */
    public function deleteFiles(Request $request, $id)
    {
        $gallery = NoticeGallary::find($id);

        if (!$gallery) {
            return response()->json(['message' => 'Record not found'], 404);
        }

        Log::debug('Delete files request data:', $request->all());

        $validator = Validator::make($request->all(), [
            'indices' => 'required_without:paths|array',
            'indices.*' => 'integer|min:0',
            'paths' => 'required_without:indices|array',
            'paths.*' => 'string|regex:/^notice_gallary\/.+$/',
        ], [
            'indices.required_without' => 'Either indices or paths must be provided.',
            'paths.required_without' => 'Either indices or paths must be provided.',
            'paths.*.regex' => 'Each path must be a valid file path starting with notice_gallary/.'
        ]);

        if ($validator->fails()) {
            Log::error('Validation failed:', $validator->errors()->toArray());
            return response()->json(['errors' => $validator->errors()], 422);
        }

        $filePaths = $gallery->Image ? json_decode($gallery->Image, true) ?? [] : [];
        if (empty($filePaths)) {
            return response()->json(['message' => 'No files to delete'], 400);
        }

        $filesToDelete = [];
        $newFilePaths = $filePaths;

        if ($request->has('indices')) {
            $indices = array_unique($request->input('indices'));
            foreach ($indices as $index) {
                if (isset($filePaths[$index])) {
                    $filesToDelete[] = $filePaths[$index];
                    unset($newFilePaths[$index]);
                } else {
                    Log::warning("Invalid index provided for deletion: {$index}");
                }
            }
        } elseif ($request->has('paths')) {
            $paths = array_unique($request->input('paths'));
            foreach ($paths as $path) {
                if (in_array($path, $filePaths)) {
                    $filesToDelete[] = $path;
                    $newFilePaths = array_diff($newFilePaths, [$path]);
                } else {
                    Log::warning("Invalid path provided for deletion: {$path}");
                }
            }
        }

        foreach ($filesToDelete as $filePath) {
            $fullPath = public_path('storage/' . $filePath);
            if (file_exists($fullPath)) {
                unlink($fullPath);
                Log::info('Deleted file', ['path' => $filePath]);
            }
        }

        $gallery->update([
            'Image' => !empty($newFilePaths) ? json_encode(array_values($newFilePaths)) : null,
        ]);

        $responseData = $gallery->toArray();
        $responseData['Image'] = !empty($gallery->Image) ? array_map(function ($path) {
            return url('/storage/' . $path);
        }, json_decode($gallery->Image, true) ?? []) : [];

        Log::info('Files deleted from gallery:', $responseData);
        return response()->json($responseData);
    }

    /**
     * Delete a NoticeGallary record by Ntg_Id.
     */
    public function destroy($id)
    {
        $gallery = NoticeGallary::find($id);

        if (!$gallery) {
            return response()->json(['message' => 'Record not found'], 404);
        }

        $files = json_decode($gallery->Image, true) ?? [];
        foreach ($files as $file) {
            $filePath = public_path('storage/' . $file);
            if (file_exists($filePath)) {
                unlink($filePath);
                Log::info('Deleted file', ['path' => $filePath]);
            }
        }

        $gallery->delete();
        return response()->json(['message' => 'Deleted successfully']);
    }
}
