<?php

namespace App\Http\Controllers\Webhooks\Communications;

use App\Enum\Contact\MessageChannel;
use App\Enum\Contact\MessageDirection;
use App\Enum\Contact\MessageProvider;
use App\Enum\Contact\MessageStatus;
use App\Facades\Notifications;
use App\Http\Controllers\Controller;
use App\Jobs\Contact\ExtractMessageAttachmentsJob;
use App\Jobs\Contact\NotifyCaseHandlersOfEmailJob;
use App\Models\Client\Client;
use App\Models\Contact\Hub\ContactMessage;
use App\Models\Contact\Hub\ContactMessageAttachment;
use App\Models\Solicitor\SolicitorEmailSender;
use App\Notifications\InboundClientEmailNotification;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class InboundEmailController extends Controller
{
    public function __invoke(Request $request, string $token): Response
    {
        $envelope = json_decode($request->input('envelope', '{}'), true);
        $toAddresses = array_map(function ($email) {
            return preg_replace('/@parse\./i', '@', $email);
        }, (array) ($envelope['to'] ?? []));
        $fromAddress = $envelope['from'] ?? $request->input('from', '');

        $fromAddress = $this->extractEmail($fromAddress);

        $sender = $this->resolveSender($toAddresses);
        if (!$sender) {
            return response('', 200);
        }

        $client = $this->resolveClient($fromAddress, $sender->solicitor_id);

        $message = ContactMessage::create([
            'solicitor_id' => $sender->solicitor_id,
            'sender_email_id' => $sender->id,
            'channel' => MessageChannel::EMAIL,
            'direction' => MessageDirection::INBOUND,
            'provider' => MessageProvider::SENDGRID,
            'status' => MessageStatus::SENT,
            'from_email' => $fromAddress,
            'recipient_email' => $sender->email_address,
            'subject' => $this->toUtf8($request->input('subject')),
            'body_html' => $this->toUtf8($request->input('html')),
            'body_text' => $this->toUtf8($request->input('text')),
            'messageable_type' => $client?->getMorphClass(),
            'messageable_id' => $client?->id,
        ]);

        if ($client) {
            $client->recordSuccessfulContactAt($message->created_at);
        }

        try {
            $this->storeAttachments($request, $message);
        } catch (\Throwable $e) {
            Log::warning('InboundEmailController: failed to store attachments for message '.$message->id, [
                'error' => $e->getMessage(),
            ]);
        }

        if ($client) {
            Notifications::sendToRole('deeplinx', new InboundClientEmailNotification($message, $client));

            // The email auto-landed on a client file — raise a task for the
            // client's case handler(s) so they're notified to review it.
            NotifyCaseHandlersOfEmailJob::dispatch($message->id);
        }

        // Extract text from any attachments first; the extraction job chains
        // to ClassifyInboundEmailJob when all attachments have been processed.
        ExtractMessageAttachmentsJob::dispatch($message->id);

        return response('', 200);
    }

    private function resolveClient(string $fromEmail, int $solicitorId): ?Client
    {
        if (!$fromEmail) {
            return null;
        }

        return Client::where('solicitor_id', $solicitorId)
            ->where('email_address', $fromEmail)
            ->first();
    }

    private function resolveSender(array $toAddresses): ?SolicitorEmailSender
    {
        foreach ($toAddresses as $raw) {
            $email = strtolower($this->extractEmail($raw));
            $sender = SolicitorEmailSender::where('email_address', $email)->first();
            if ($sender) {
                return $sender;
            }
        }

        return null;
    }

    private function toUtf8(?string $value): ?string
    {
        if ($value === null) {
            return null;
        }

        if (mb_check_encoding($value, 'UTF-8')) {
            return $value;
        }

        // Outlook / Windows mail clients often send bodies encoded as Windows-1252.
        // Convert to UTF-8 so MySQL's utf8/utf8mb4 columns can store the content.
        return mb_convert_encoding($value, 'UTF-8', 'Windows-1252');
    }

    private function extractEmail(string $raw): string
    {
        if (preg_match('/<([^>]+)>/', $raw, $m)) {
            return strtolower(trim($m[1]));
        }

        return strtolower(trim($raw));
    }

    private function storeAttachments(Request $request, ContactMessage $message): void
    {
        $attachmentCount = (int) $request->input('attachments', 0);
        if ($attachmentCount === 0) {
            return;
        }

        $info = json_decode($request->input('attachment-info', '{}'), true);

        for ($i = 1; $i <= $attachmentCount; $i++) {
            $file = $request->file("attachment{$i}");
            if (!$file) {
                continue;
            }

            $meta = $info["attachment{$i}"] ?? [];
            $filename = $this->toUtf8($meta['filename'] ?? $file->getClientOriginalName()) ?? 'attachment';
            $mime = $meta['type'] ?? $file->getMimeType();
            $size = $file->getSize();

            $path = $file->storeAs(
                "contact-attachments/{$message->id}",
                $filename,
            );

            ContactMessageAttachment::create([
                'contact_message_id' => $message->id,
                'disk' => config('filesystems.default'),
                'path' => $path,
                'filename' => $filename,
                'mime' => $mime,
                'size' => $size,
            ]);
        }
    }
}
