<?php

namespace App\Http\Controllers;

use App\Models\Admin\Pricing;
use App\Models\CustomersPlans;
use App\Models\Files;
use App\Models\Modules;
use Carbon\Carbon;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use ZipArchive;

class FilesController extends Controller
{
    /**
     * Get current plan based on customer
     */
    private function getUserCurrentPlan($customer_id)
    {
        $get_current_plan = CustomersPlans::select('plan_id')->where('customer_id', $customer_id)->orderByDesc('id')->first();
        return $get_current_plan->plan_id ?? 0;
    }

    /**
     * Get max allowed size based on plan
     */
    private function getPlanMaxSize($plan_id)
    {
        if ($plan_id == 0) {
            return 1024 * 1024 * 1024;
        }

        $getPlan = Pricing::select();
        return 2 * 1024 * 1024 * 1024;

        // switch ($plan) {
        //     case 'free': return 10 * 1024 * 1024; // 10 MB
        //     case 'pro':  return 100 * 1024 * 1024; // 100 MB
        //     case 'elite':return 1024 * 1024 * 1024; // 1 GB
        //     default:     return 5 * 1024 * 1024; // fallback 5 MB
        // }
    }

    /**
     * Helper to format bytes
     */
    private function formatBytes($bytes, $precision = 2)
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];

        $bytes = max($bytes, 0);
        if ($bytes === 0) {
            return '0 B';
        }

        $pow = floor(log($bytes, 1024));
        $pow = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);

        return round($bytes, $precision) . ' ' . $units[$pow];
    }

    private function updateModuleDownloadStatus(string $uuid, bool $all = false)
    {
        $module = Modules::where('uuid', $uuid)->first();
        if (!$module) {
            return;
        }

        if ($all) {
            // When all files are downloaded (ZIP case)
            $module->update(['status' => 'downloaded_expired']);
            return;
        }

        // When single file downloaded — check if all files are done
        $totalFiles = Files::where('uuid', $uuid)->count();
        $downloadedFiles = Files::where('uuid', $uuid)
            ->where('file_status', 'downloaded')
            ->count();

        if ($totalFiles === $downloadedFiles) {
            $module->update(['status' => 'downloaded_expired']);
        } else {
            $module->update(['status' => 'partially_downloaded']);
        }
    }


    public function createModule(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'send_option'  => ['required', 'string', 'max:100'],
            'email_to'    => ['nullable', 'email', 'max:100'],
            'your_email'  => ['required', 'email', 'max:100'],
            'total_size'  => ['required', 'numeric'],
            'terms'  => ['accepted'],
            'message'     => ['nullable', 'string', 'max:1000'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 'error',
                'errors' => $validator->errors(),
            ], 422);
        }

        try {
            $defaultSize = 1 * 1024 * 1024 * 1024;
            $customer_id = $request->customer_id ?? 0;

            try {
                if ($customer_id != 0) {
                    $customer_id = Crypt::decrypt($request->customer_id);
                    $userPlanId = $this->getUserCurrentPlan($customer_id);
                    $maxAllowedSize = $userPlanId === 0 ? $defaultSize : $this->getPlanMaxSize($userPlanId);
                } else {
                    $maxAllowedSize = $defaultSize;
                }
            } catch (\Exception $e) {
                return response()->json([
                    'status'  => 'error',
                    'message' => 'Invalid Customer ID.',
                ], 400);
            }

            if ($request->total_size > $maxAllowedSize) {
                return response()->json([
                    'status'  => 'error',
                    'message' => 'Total file size exceeds plan limit of ' . $this->formatBytes($maxAllowedSize),
                ], 403);
            }

            $uuid = Str::uuid()->toString();
            $passcode = str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT);
            $sharable_link = "/share/{$uuid}";

            $module = Modules::create([
                'customer_id'   => $customer_id,
                'uuid'          => $uuid,
                'send_option'   => $request->send_option,
                'email_to'      => $request->email_to,
                'your_email'    => $request->your_email,
                'terms'         => $request->terms,
                'passcode'      => $passcode,
                'sharable_link' => $sharable_link,
                'creation'      => Carbon::now('Asia/Kolkata'),
                'validity'      => Carbon::now('Asia/Kolkata')->addHour(),
                'status'        => "activated",
                'message'       => $request->message,
            ]);

            if (!$module) {
                return response()->json([
                    'status'  => 'error',
                    'message' => 'Failed to create module.',
                ], 500);
            }

            $module_id = Crypt::encrypt($module->id);

            return response()->json([
                'status'        => 'success',
                'message'       => 'Processing Done.',
                'uuid'          => $uuid,
                'moduleId'      => $module_id,
                'passcode'      => $passcode,
                'sharableLink'  => $sharable_link,
            ], 201);
        } catch (\Throwable $e) {
            Log::error('Module creation failed: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            return response()->json([
                'status'  => 'error',
                'message' => 'Something went wrong. Please try again later.',
            ], 500);
        }
    }

    /**
     * Chunked file upload handler
     */
    public function uploadChunk(Request $request)
    {
        // Validate chunk upload
        $validator = Validator::make($request->all(), [
            'file'        => ['required', 'file'],
            'fileName'    => ['required', 'string'],
            'chunkIndex'  => ['required', 'integer'],
            'uuid'        => ['required', 'string'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 'error',
                'errors' => $validator->errors(),
            ], 422);
        }

        try {
            $uuid = $request->uuid;
            $fileName = $request->fileName;
            $chunkIndex = $request->chunkIndex;
            $file = $request->file('file');

            // Ensure directory exists
            $chunkPath = "chunks/{$uuid}/{$fileName}";
            Storage::disk('local')->makeDirectory($chunkPath);

            // Save chunk
            Storage::disk('local')->putFileAs($chunkPath, $file, "chunk_{$chunkIndex}");

            return response()->json([
                'status'  => 'success',
                'message' => "Chunk {$chunkIndex} uploaded successfully."
            ]);
        } catch (\Throwable $e) {
            Log::error('Chunk upload failed: ' . $e->getMessage());
            return response()->json([
                'status'  => 'error',
                'message' => 'Failed to upload chunk.',
            ], 500);
        }
    }

    /**
     * Merge chunks into a single file
     */
    public function mergeChunks(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'fileName'  => ['required', 'string'],
            'module_id' => ['required', 'string'],
            'uuid'      => ['required', 'string'],
            'passcode'  => ['required', 'string'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 'error',
                'errors' => $validator->errors(),
            ], 422);
        }

        try {
            $uuid = $request->uuid;
            $fileName = $request->fileName;
            $module_id = Crypt::decrypt($request->module_id);

            $chunkFolder = "chunks/{$uuid}/{$fileName}";

            if (!Storage::disk('local')->exists($chunkFolder)) {
                return response()->json(['error' => 'Chunks not found'], 404);
            }

            // Get chunk files sorted
            $chunks = Storage::disk('local')->files($chunkFolder);
            natsort($chunks);

            // Generate encrypted filename (preserve extension)
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);
            $encryptedName = Str::random(40) . ($extension ? ".{$extension}" : "");

            // Prepare final storage path
            $finalPath = "uploads/" . date('Y/m/d') . "/" . $fileName;
            $finalFullPath = storage_path("app/public/{$finalPath}");
            @mkdir(dirname($finalFullPath), 0755, true);

            $out = fopen($finalFullPath, "ab");

            foreach ($chunks as $chunkFile) {
                $chunkStream = Storage::disk('local')->readStream($chunkFile);
                stream_copy_to_stream($chunkStream, $out);
                fclose($chunkStream);
            }

            fclose($out);

            // Delete chunks after merge
            Storage::disk('local')->deleteDirectory($chunkFolder);

            // Save file in DB
            $file_model = Files::create([
                'module_id'     => $module_id,
                'uuid'          => $uuid,
                'original_name' => $fileName,
                'encrypted_name' => $encryptedName,
                'passcode'      => $request->passcode,
                'file_status'   => 'created',
                'status'        => 'added',
                'size_bytes'    => filesize($finalFullPath),
                'mime_type'     => mime_content_type($finalFullPath),
                'scan_result'   => 'Not Scanning',
                'scanned_at'    => now(),
                'stored_path'   => $finalPath,
            ]);

            return response()->json([
                'status'      => 'success',
                'message'     => 'File uploaded and merged successfully',
                'file_id'     => $file_model->id,
                'stored_path' => $finalPath,
            ]);
        } catch (\Throwable $e) {
            Log::error('Merge failed: ' . $e->getMessage());
            return response()->json([
                'status'  => 'error',
                'message' => 'Failed to merge chunks.',
            ], 500);
        }
    }

    public function updateModule(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'module_id'  => ['required', 'string'],
            'status'  => ['required', 'string'],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 'error',
                'errors' => $validator->errors(),
            ], 422);
        }

        try {
            $module_id = Crypt::decrypt($request->module_id);
            $module = Modules::find($module_id);

            if (!$module) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Module not found.',
                ], 404);
            }

            $module->status = $request->status;
            $module->updated_at = now();
            $module->save();

            return response()->json([
                'status'        => 'success',
                'message'       => 'Module is updated successfully.',
            ], 200);
        } catch (\Throwable $e) {
            Log::error('Module updation failed: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            return response()->json([
                'status'  => 'error',
                'message' => 'Something went wrong. Please try again later.',
            ], 500);
        }
    }

    public function checkLink(string $uuid)
    {
        $module = Modules::where('uuid', $uuid)->first();

        if (!$module) {
            return response()->json(['validity' => false, 'error' => 'Not found'], 404);
        }

        if ($module->status == 'downloaded_expired') {
            return response()->json(['validity' => false, 'error' => 'Invalid Link!'], 410);
        } elseif ($module->validity < Carbon::now('Asia/Kolkata')) {
            $get_files = Files::where('module_id', $module->id)->get();
            if ($get_files->count() > 0) {
                $paths = $get_files->pluck('stored_path')->toArray();
                Storage::disk('public')->delete($paths);

                Files::where('module_id', $module->id)->update(['file_status' => 'removed', 'status' => 'deleted']);
            }
            $module->update(['status' => 'expired']);

            return response()->json(['validity' => false, 'error' => 'Invalid Link!'], 410);
        }

        $module->increment('total_clicks');
        return response()->json(['validity' => true]);
    }

    public function checkLinkByPasscode(string $passcode)
    {
        $module = Modules::where('passcode', $passcode)->first();

        if (!$module) {
            return response()->json(['validity' => false, 'error' => 'Passcode Not found'], 404);
        }

        if ($module->status == 'downloaded_expired') {
            return response()->json(['validity' => false, 'error' => 'Invalid Link!'], 410);
        } elseif ($module->validity < Carbon::now('Asia/Kolkata')) {
            $get_files = Files::where('module_id', $module->id)->get();
            if ($get_files->count() > 0) {
                $paths = $get_files->pluck('stored_path')->toArray();
                Storage::disk('public')->delete($paths);

                Files::where('module_id', $module->id)->update(['file_status' => 'removed', 'status' => 'deleted']);
            }
            $module->update(['status' => 'expired']);

            return response()->json(['validity' => false, 'error' => 'Link is expired!'], 410);
        }

        $module->increment('total_clicks');
        return response()->json(['validity' => true, 'uuid' => $module->uuid, 'sharable_link' => $module->sharable_link]);
    }

    public function share(string $uuid)
    {
        $file = Files::where('uuid', $uuid)->first();

        if (!$file) {
            return response()->json(['error' => 'Not found'], 404);
        }

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

    public function getFiles(string $uuid)
    {
        $files = Files::where(['uuid' => $uuid, 'file_status' => 'created', 'status' => 'added'])->get();

        if ($files->isEmpty()) {
            return response()->json(['error' => 'Not found'], 404);
        }

        return response()->json([
            'files' => $files->map(function ($f) {
                return [
                    'id'          => Crypt::encrypt($f->id),
                    'name'        => $f->original_name,
                    'file_status' => $f->file_status,
                    'status'      => $f->status,
                    'scan_result' => $f->scan_result,
                    'download'    => $f->status === 'added'
                        ? asset("storage/{$f->stored_path}")
                        : null,
                ];
            }),
        ]);
    }

    public function getSingleFile(string $fileId)
    {
        try {
            $id = Crypt::decrypt($fileId);
        } catch (\Exception $e) {
            return response()->json(['error' => 'Invalid file identifier'], 400);
        }
        $file = Files::find($id);

        if (!$file) {
            return response()->json(['error' => 'File Not found'], 404);
        }

        if ($file->file_status === 'removed' || $file->status === 'deleted') {
            return response()->json(['error' => 'File has been removed or expired'], 410); // 410 Gone
        }

        // Check existence in storage
        if (!Storage::disk('public')->exists($file->stored_path)) {
            return response()->json(['error' => 'File missing in storage'], 404);
        }

        $file->update(['file_status' => 'downloaded', 'status' => 'deleted']);
        $file->increment('total_downloads');

        $this->updateModuleDownloadStatus($file->uuid);

        $path = storage_path("app/public/{$file->stored_path}");
        $fileName = $file->original_name;

        register_shutdown_function(function () use ($file) {
            Storage::disk('public')->delete($file->stored_path);
        });

        return response()->download($path, $fileName, ['Content-Type' => mime_content_type($path)])->deleteFileAfterSend(false);
    }

    public function downloadFiles(string $uuid)
    {
        $files = Files::where('uuid', $uuid)->where(['file_status' => 'created', 'status' => 'added'])->get();

        if ($files->isEmpty()) {
            return response()->json(['error' => 'No files found'], 404);
        }

        $zip = new ZipArchive;
        $zipFileName = "room_{$uuid}_files_" . time() . ".zip";
        $zipPath = storage_path("app/public/{$zipFileName}");

        if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            return response()->json(['error' => 'Could not create ZIP file'], 500);
        }

        foreach ($files as $file) {
            if (Storage::disk('public')->exists($file->stored_path)) {
                $absolutePath = Storage::disk('public')->path($file->stored_path);
                $zip->addFile($absolutePath, $file->original_name);

                $file->update(['file_status' => 'downloaded', 'status' => 'deleted']);
                $file->increment('total_downloads');
            }
        }
        $zip->close();

        $this->updateModuleDownloadStatus($uuid, true);

        register_shutdown_function(function () use ($files, $zipPath) {
            foreach ($files as $file) {
                if (Storage::disk('public')->exists($file->stored_path)) {
                    Storage::disk('public')->delete($file->stored_path);
                }
            }
            if (file_exists($zipPath)) {
                @unlink($zipPath);
            }
        });

        return response()->download($zipPath, $zipFileName);
    }

    public function getCustomerData(Request $request)
    {
        try {
            $customer_id = Crypt::decrypt($request->customer_id);
        } catch (\Exception $e) {
            return response()->json(['error' => 'Invalid Customer Id'], 400);
        }

        $perPage = $request->input('per_page', 10);

        $query = Modules::where('customer_id', $customer_id);
        $totalCount = $query->count();

        $modules = $query->select('email_to', 'creation', 'validity', 'total_clicks', 'sharable_link', 'passcode', 'status')
            ->orderByDesc('id')
            ->paginate($perPage);

        if ($modules->isEmpty()) {
            return response()->json(['error' => 'Data Not found'], 404);
        }

        $modules->getCollection()->transform(function ($module, $i) {
            if ($module->status == 'downloaded_expired') {
                $module_status = 'Downloaded & Expired';
            } elseif ($module->status == 'partially_downloaded') {
                $module_status = 'Partially Downloaded';
            } elseif ($module->status == 'uploaded') {
                $module_status = 'Uploaded';
            } elseif ($module->status == 'activated') {
                $module_status = 'Activated';
            } else {
                $module_status = 'Expired';
            }
            return [
                'sno'           => $i + 1,
                'email_to'      => $module->email_to,
                'total_clicks'  => $module->total_clicks,
                'sharable_link'  => $module->sharable_link,
                'passcode'  => $module->passcode,
                'creation'      => $module->creation ? Carbon::parse($module->creation, 'Asia/Kolkata')->format('h:i:s A, M d, Y') : Carbon::now('Asia/Kolkata')->format('h:i:s A, M d, Y'),
                'validity'      => $module->validity ? Carbon::parse($module->validity, 'Asia/Kolkata')->format('h:i:s A, M d, Y') : Carbon::now('Asia/Kolkata')->format('h:i:s A, M d, Y'),
                'isValid' => ($module->validity ? !Carbon::parse($module->validity, 'Asia/Kolkata')->isPast() : false),
                'module_status'  => $module_status
            ];
        });

        return response()->json([
            'total_count'  => $totalCount,
            'per_page'     => $modules->perPage(),
            'current_page' => $modules->currentPage(),
            'last_page'    => $modules->lastPage(),
            'data_count'   => $modules->count(),
            'data'         => $modules->items(),
        ], 200);
    }

    public function sendShareEmail(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'module_id' => 'required|string',
            'to' => 'required|email',
            'from' => 'nullable|email',
            'share_link' => 'required|string',
            'passcode' => 'required|string',
            'qr_image' => 'required|string',
            'message' => 'nullable|string',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 'error',
                'errors' => $validator->errors(),
            ], 422);
        }

        try {
            $module_id = Crypt::decrypt($request->module_id);
            $current_module = Modules::select('validity')->findOrFail($module_id);

            $expiry_date = Carbon::parse($current_module->validity)->format('F j, Y g:i A');

            $tempPath = storage_path('app/temp');
            if (!File::exists($tempPath)) {
                File::makeDirectory($tempPath, 0775, true);
            }

            $qrData = $request->qr_image;
            $qrData = str_replace('data:image/png;base64,', '', $qrData);
            $qrData = str_replace(' ', '+', $qrData);

            $qrFileName = 'qr_' . Str::random(8) . '.png';
            $qrFilePath = $tempPath . '/' . $qrFileName;
            file_put_contents($qrFilePath, base64_decode($qrData));

            Mail::send('emails.share-file', [
                'share_link'  => $request->share_link,
                'passcode'    => $request->passcode,
                'messageText' => $request->message ?? 'Files have been shared with you.',
                'qr_cid' => $qrFileName,
                'expiry_date' => $expiry_date,
            ], function ($m) use ($request, $qrFilePath) {
                $m->to($request->to)
                    ->from(
                        $request->from ?? env('MAIL_FROM_ADDRESS'),
                        env('APP_NAME_FOR_MAIL', config('app.name', 'MyApp'))
                    )
                    ->subject('Your Secure Data Transfer is Ready ✅')
                    ->embed($qrFilePath);
            });

            // Delete file after sending (optional cleanup)
            @unlink($qrFilePath);

            return response()->json([
                'status' => 'success',
                'message' => 'Email sent successfully.',
            ], 200);
        } catch (Exception $e) {
            Log::error('Mail sending failed: ' . $e->getMessage());

            return response()->json([
                'status' => 'error',
                'message' => 'Failed to send email. Please try again later.',
                'error' => $e->getMessage(),
            ], 500);
        }
    }
}
