<?php

namespace App\Http\Controllers;

use App\Events\NewInboxMessage;
use App\Models\Contact;
use App\Models\Conversation;
use App\Models\Message;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Netflie\WhatsAppCloudApi\WebHook;

class WebhookController extends Controller
{
    /**
     * GET /webhook/whatsapp?token={token}
     * Meta calls this to verify the webhook.
     *
     * Meta sends: ?token=X&hub.mode=subscribe&hub.verify_token=X&hub.challenge=CHALLENGE
     * PHP converts hub.mode → hub_mode, hub.verify_token → hub_verify_token, etc.
     * We verify the token manually to avoid http_response_code() quirks with Laravel.
     */
    public function verify(Request $request)
    {
        $token = $request->query('token');
        $user  = User::where('api_token', $token)->first();

        if (!$user) {
            return response('Invalid token.', 403);
        }

        // PHP parses hub.mode as hub_mode, hub.verify_token as hub_verify_token, etc.
        $mode      = $request->query('hub_mode');
        $hubToken  = $request->query('hub_verify_token');
        $challenge = $request->query('hub_challenge', '');

        // No hub.mode = just a browser/health check, return OK
        if (!$mode) {
            return response()->json(['status' => 'ok', 'message' => 'Webhook is live.']);
        }

        // Verify: mode must be 'subscribe' and hub.verify_token must match api_token
        if ($mode === 'subscribe' && $hubToken === $token) {
            return response($challenge, 200)->header('Content-Type', 'text/plain');
        }

        Log::warning('Webhook verify failed', [
            'mode'      => $mode,
            'hub_token' => $hubToken,
            'expected'  => $token,
        ]);

        return response('Verification failed.', 403);
    }

    /**
     * POST /webhook/whatsapp?token={token}
     * Meta sends incoming messages here.
     */
    public function handle(Request $request)
    {
        $token = $request->query('token');
        $user  = User::where('api_token', $token)->first();

        if (!$user) {
            return response('Invalid token.', 403);
        }

        $payload = $request->all();

        try {
            $webhook      = new WebHook();
            $notification = $webhook->read($payload);

            if (!$notification) {
                return response('EVENT_RECEIVED', 200);
            }

            $from = method_exists($notification, 'from') ? $notification->from() : null;
            $type = $notification->type();
            $waMessageId = method_exists($notification, 'messageId') ? $notification->messageId() : null;

            if (!$from) {
                return response('EVENT_RECEIVED', 200);
            }

            // Find or create contact for this phone number
            $contact = Contact::firstOrCreate(
                ['user_id' => $user->id, 'phone' => $from],
                ['name' => $from]
            );

            // Find or create conversation, linked to the contact
            $conversation = Conversation::firstOrCreate(
                ['user_id' => $user->id, 'contact_phone' => $from],
                ['contact_id' => $contact->id, 'contact_name' => $contact->name, 'status' => 'new']
            );

            // Backfill contact_id for conversations created before contacts system
            if (!$conversation->contact_id) {
                $conversation->update(['contact_id' => $contact->id]);
            }

            // Parse content based on message type
            $content       = null;
            $mediaUrl      = null;
            $mediaFilename = null;

            switch ($type) {
                case 'text':
                    $content = method_exists($notification, 'message')
                        ? $notification->message()->body()
                        : null;
                    break;

                case 'image':
                case 'video':
                case 'audio':
                case 'document':
                case 'sticker':
                    $mediaObj = match ($type) {
                        'image'    => method_exists($notification, 'image')    ? $notification->image()    : null,
                        'video'    => method_exists($notification, 'video')    ? $notification->video()    : null,
                        'audio'    => method_exists($notification, 'audio')    ? $notification->audio()    : null,
                        'document' => method_exists($notification, 'document') ? $notification->document() : null,
                        'sticker'  => method_exists($notification, 'sticker')  ? $notification->sticker()  : null,
                        default    => null,
                    };

                    if ($mediaObj && method_exists($mediaObj, 'id')) {
                        $mediaId = $mediaObj->id();
                        // Caption for image/video
                        if (method_exists($mediaObj, 'caption')) {
                            $content = $mediaObj->caption();
                        }
                        // Original filename for documents
                        if ($type === 'document' && method_exists($mediaObj, 'filename')) {
                            $mediaFilename = $mediaObj->filename();
                        }
                        [$mediaUrl, $mediaFilename] = $this->downloadMedia($user, $mediaId, $type, $mediaFilename);
                    }
                    break;

                default:
                    $content = "[{$type}]";
            }

            // Skip duplicate messages
            if ($waMessageId && Message::where('wa_message_id', $waMessageId)->exists()) {
                return response('EVENT_RECEIVED', 200);
            }

            // Save message
            $message = Message::create([
                'conversation_id' => $conversation->id,
                'user_id'         => $user->id,
                'direction'       => 'incoming',
                'type'            => $type,
                'content'         => $content,
                'media_url'       => $mediaUrl,
                'media_filename'  => $mediaFilename,
                'wa_message_id'   => $waMessageId,
                'status'          => 'delivered',
            ]);

            // Update conversation
            $preview = $content ?? (new Message(['type' => $type]))->mediaLabel();
            $conversation->update([
                'last_message'              => $preview,
                'last_message_at'           => now(),
                'customer_last_message_at'  => now(),
                'unread_count'              => $conversation->unread_count + 1,
                'status'                    => $conversation->status === 'closed' ? 'open' : $conversation->status,
            ]);

            // Broadcast to Pusher so the inbox updates in real-time
            broadcast(new NewInboxMessage($message, $conversation->fresh()));

            Log::info('WhatsApp message saved', [
                'user_id'         => $user->id,
                'from'            => $from,
                'type'            => $type,
                'conversation_id' => $conversation->id,
            ]);

        } catch (\Throwable $e) {
            Log::error('WhatsApp Webhook Error', [
                'user_id' => $user->id,
                'error'   => $e->getMessage(),
                'payload' => $payload,
            ]);
        }

        return response('EVENT_RECEIVED', 200);
    }

    /**
     * Download WhatsApp media and store it (local or S3 based on settings).
     * Returns [storedUrl, filename].
     */
    private function downloadMedia(User $user, string $mediaId, string $type, ?string $origFilename = null): array
    {
        try {
            if (!$user->access_token) return [null, null];

            // Step 1 — get download URL from Meta
            $meta = Http::withToken($user->access_token)
                ->get("https://graph.facebook.com/v17.0/{$mediaId}");

            if (!$meta->ok()) return [null, null];

            $downloadUrl = $meta->json('url');
            $mimeType    = $meta->json('mime_type', '');

            if (!$downloadUrl) return [null, null];

            // Step 2 — download the file bytes
            $fileRes = Http::withToken($user->access_token)->get($downloadUrl);
            if (!$fileRes->ok()) return [null, null];

            $ext      = $this->mimeToExt($mimeType, $type);
            $filename = $origFilename ?? "wa_{$mediaId}.{$ext}";
            $path     = "inbox/{$user->id}/{$filename}";

            // Step 3 — store based on file_storage setting
            $storageType = Setting::get('file_storage', 'local');

            if ($storageType === 's3' || $storageType === 'spaces') {
                Storage::disk('s3')->put($path, $fileRes->body(), 'public');
                $url = Storage::disk('s3')->url($path);
            } else {
                Storage::disk('public')->put($path, $fileRes->body());
                $url = asset('storage/' . $path);
            }

            return [$url, $filename];

        } catch (\Throwable $e) {
            Log::error('Media download failed', ['media_id' => $mediaId, 'error' => $e->getMessage()]);
            return [null, null];
        }
    }

    private function mimeToExt(string $mimeType, string $fallbackType): string
    {
        return match ($mimeType) {
            'image/jpeg'                                                             => 'jpg',
            'image/png'                                                              => 'png',
            'image/webp'                                                             => 'webp',
            'image/gif'                                                              => 'gif',
            'audio/ogg', 'audio/ogg; codecs=opus'                                   => 'ogg',
            'audio/mpeg'                                                             => 'mp3',
            'audio/mp4'                                                              => 'm4a',
            'video/mp4'                                                              => 'mp4',
            'video/3gpp'                                                             => '3gp',
            'application/pdf'                                                        => 'pdf',
            'application/vnd.ms-powerpoint'                                          => 'ppt',
            'application/msword'                                                     => 'doc',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
            'image/webp; codecs=vp8'                                                 => 'webp',
            default => match ($fallbackType) {
                'image'    => 'jpg',
                'audio'    => 'ogg',
                'video'    => 'mp4',
                'document' => 'pdf',
                'sticker'  => 'webp',
                default    => 'bin',
            },
        };
    }
}
