<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\StoreEmailTemplateRequest;
use App\Jobs\SendEmailTemplateJob;
use App\Models\Activity;
use App\Models\Client;
use App\Models\ClientCase;
use App\Models\Contract;
use App\Models\Document;
use App\Models\EmailLog;
use App\Models\EmailLogStatus;
use App\Models\EmailTemplate;
use App\Models\MailProfile;
use App\Services\EmailTemplateRenderer;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Inertia\Response;
use Symfony\Component\Mime\Email;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;

class EmailTemplateController extends Controller
{
    use AuthorizesRequests;

    public function update(\App\Http\Requests\UpdateEmailTemplateRequest $request, EmailTemplate $emailTemplate)
    {
        $this->authorize('update', $emailTemplate);
        $data = $request->validated();
        $emailTemplate->fill($data)->save();
        // Move any tmp images referenced in HTML into permanent storage and attach as documents
        $this->adoptTmpImages($emailTemplate);

        return redirect()->route('admin.email-templates.edit', $emailTemplate)->with('success', 'Template updated');
    }

    use AuthorizesRequests;

    public function index(): Response
    {
        $this->authorize('viewAny', EmailTemplate::class);

        return Inertia::render('Admin/EmailTemplates/Index', [
            'templates' => EmailTemplate::orderBy('name')->get(),
        ]);
    }

    public function create(): Response
    {
        $this->authorize('create', EmailTemplate::class);

        $actions = \App\Models\Action::query()
            ->with(['decisions:id,name'])
            ->orderBy('name')
            ->get(['id', 'name']);

        return Inertia::render('Admin/EmailTemplates/Edit', [
            'template' => null,
            'actions' => $actions,
        ]);
    }

    public function store(StoreEmailTemplateRequest $request)
    {
        $data = $request->validated();
        $tpl = EmailTemplate::create($data);
        // Move any tmp images referenced in HTML into permanent storage and attach as documents
        $this->adoptTmpImages($tpl);

        return redirect()->route('admin.email-templates.edit', $tpl)->with('success', 'Template created');
    }

    /**
     * Render a quick preview of the email template with the provided context.
     * Does not persist any changes or inline CSS; intended for fast editor feedback.
     */
    public function preview(Request $request, EmailTemplate $emailTemplate): JsonResponse
    {
        $this->authorize('view', $emailTemplate);

        $renderer = app(EmailTemplateRenderer::class);
        $subject = (string) ($request->input('subject') ?? $emailTemplate->subject_template);
        $html = (string) ($request->input('html') ?? $emailTemplate->html_template);
        $text = (string) ($request->input('text') ?? $emailTemplate->text_template);

        // Do not persist tmp images for preview, but allow showing them if already accessible
        // Optionally repair missing img src and attach from template documents for a better preview
        if (! empty($html)) {
            $html = $this->repairImgWithoutSrc($html);
            $html = $this->attachSrcFromTemplateDocuments($emailTemplate, $html);
        }

        // Context resolution (shared logic with renderFinalHtml)
        $ctx = [];
        if ($id = $request->integer('activity_id')) {
            $activity = Activity::query()->with(['action', 'decision', 'contract.clientCase.client.person', 'contract.account', 'clientCase.client.person'])->find($id);
            if ($activity) {
                $ctx['activity'] = $activity;
                // Derive base entities from activity when not explicitly provided
                if ($activity->contract && ! isset($ctx['contract'])) {
                    $ctx['contract'] = $activity->contract;
                }
                if ($activity->clientCase && ! isset($ctx['client_case'])) {
                    $ctx['client_case'] = $activity->clientCase;
                }
                if (($ctx['client_case'] ?? null) && ! isset($ctx['client'])) {
                    $ctx['client'] = $ctx['client_case']->client;
                    $ctx['person'] = optional($ctx['client'])->person;
                }
            }
        }
        if ($id = $request->integer('contract_id')) {
            $contract = Contract::query()->with(['clientCase.client.person', 'account'])->find($id);
            if ($contract) {
                $ctx['contract'] = $contract;
                if ($contract->clientCase) {
                    $ctx['client_case'] = $contract->clientCase;
                    if ($contract->clientCase->client) {
                        $ctx['client'] = $contract->clientCase->client;
                        $ctx['person'] = optional($contract->clientCase->client)->person;
                    }
                }
            }
        }
        if (! isset($ctx['client_case']) && ($id = $request->integer('case_id'))) {
            $case = ClientCase::query()->with(['client.person'])->find($id);
            if ($case) {
                $ctx['client_case'] = $case;
                if ($case->client) {
                    $ctx['client'] = $case->client;
                    $ctx['person'] = optional($case->client)->person;
                }
            }
        }
        if (! isset($ctx['client']) && ($id = $request->integer('client_id'))) {
            $client = Client::query()->with(['person'])->find($id);
            if ($client) {
                $ctx['client'] = $client;
                $ctx['person'] = optional($client)->person;
            }
        }
        $ctx['extra'] = (array) $request->input('extra', []);
        $ctx['mail_profile'] = MailProfile::query()->orderBy('active', 'desc')->orderBy('priority')->orderBy('id')->first();

        $rendered = $renderer->render([
            'subject' => $subject,
            'html' => $html,
            'text' => $text,
        ], $ctx);

        return response()->json([
            'subject' => $rendered['subject'] ?? $subject,
            'html' => (string) ($rendered['html'] ?? $html ?? ''),
            'text' => (string) ($rendered['text'] ?? $text ?? ''),
        ]);
    }

    public function edit(EmailTemplate $emailTemplate): Response
    {
        $this->authorize('update', $emailTemplate);
        $emailTemplate->load(['documents' => function ($q) {
            $q->select(['id', 'documentable_id', 'documentable_type', 'name', 'path', 'size', 'created_at']);
        }]);

        $actions = \App\Models\Action::query()
            ->with(['decisions:id,name'])
            ->orderBy('name')
            ->get(['id', 'name']);

        return Inertia::render('Admin/EmailTemplates/Edit', [
            'template' => $emailTemplate,
            'actions' => $actions,
        ]);
    }

    public function sendTest(Request $request, EmailTemplate $emailTemplate)
    {
        $this->authorize('send', $emailTemplate);

        $renderer = app(EmailTemplateRenderer::class);
        $subject = (string) ($request->input('subject') ?? $emailTemplate->subject_template);
        $html = (string) ($request->input('html') ?? $emailTemplate->html_template);
        $text = (string) ($request->input('text') ?? $emailTemplate->text_template);

        // Adopt tmp images (tmp/email-images) so test email can display images; also persist
        $html = $this->adoptTmpImagesInHtml($emailTemplate, $html, true);

        // Context resolution
        $ctx = [];
        if ($id = $request->integer('activity_id')) {
            $activity = Activity::query()->with(['action', 'decision', 'contract.clientCase.client.person', 'contract.account', 'clientCase.client.person'])->find($id);
            if ($activity) {
                $ctx['activity'] = $activity;
                if ($activity->contract && ! isset($ctx['contract'])) {
                    $ctx['contract'] = $activity->contract;
                }
                if ($activity->clientCase && ! isset($ctx['client_case'])) {
                    $ctx['client_case'] = $activity->clientCase;
                }
                if (($ctx['client_case'] ?? null) && ! isset($ctx['client'])) {
                    $ctx['client'] = $ctx['client_case']->client;
                    $ctx['person'] = optional($ctx['client'])->person;
                }
            }
        }
        if ($id = $request->integer('contract_id')) {
            $contract = Contract::query()->with(['clientCase.client.person', 'account'])->find($id);
            if ($contract) {
                $ctx['contract'] = $contract;
                if ($contract->clientCase) {
                    $ctx['client_case'] = $contract->clientCase;
                    if ($contract->clientCase->client) {
                        $ctx['client'] = $contract->clientCase->client;
                        $ctx['person'] = optional($contract->clientCase->client)->person;
                    }
                }
            }
        }
        if (! isset($ctx['client_case']) && ($id = $request->integer('case_id'))) {
            $case = ClientCase::query()->with(['client.person'])->find($id);
            if ($case) {
                $ctx['client_case'] = $case;
                if ($case->client) {
                    $ctx['client'] = $case->client;
                    $ctx['person'] = optional($case->client)->person;
                }
            }
        }
        if (! isset($ctx['client']) && ($id = $request->integer('client_id'))) {
            $client = Client::query()->with(['person'])->find($id);
            if ($client) {
                $ctx['client'] = $client;
                $ctx['person'] = optional($client)->person;
            }
        }
        $ctx['extra'] = (array) $request->input('extra', []);
        $ctx['mail_profile'] = MailProfile::query()->orderBy('active', 'desc')->orderBy('priority')->orderBy('id')->first();

        // Render preview values; we store a minimal snapshot on the log
        $rendered = $renderer->render([
            'subject' => $subject,
            'html' => $html,
            'text' => $text,
        ], $ctx);

        $to = (string) $request->input('to');
        if ($to === '' || ! filter_var($to, FILTER_VALIDATE_EMAIL)) {
            return back()->with('error', 'Invalid target email');
        }

        // Prepare EmailLog record with queued status
        $log = new EmailLog;
        $log->fill([
            'uuid' => (string) \Str::uuid(),
            'template_id' => $emailTemplate->id,
            'to_email' => $to,
            'to_name' => null,
            'subject' => (string) ($rendered['subject'] ?? $subject ?? ''),
            'body_html_hash' => $rendered['html'] ? hash('sha256', $rendered['html']) : null,
            'body_text_preview' => isset($rendered['text']) ? mb_strimwidth((string) $rendered['text'], 0, 4096) : null,
            'embed_mode' => (string) $request->input('embed', 'base64'),
            'status' => EmailLogStatus::Queued,
            'queued_at' => now(),
            'client_id' => $ctx['client']->id ?? null,
            'client_case_id' => $ctx['client_case']->id ?? null,
            'contract_id' => $ctx['contract']->id ?? null,
            'extra_context' => $ctx['extra'] ?? null,
            'ip' => $request->ip(),
        ]);
        $log->save();

        // Store bodies in companion table (optional, enabled here)
        $log->body()->create([
            'body_html' => (string) ($rendered['html'] ?? ''),
            'body_text' => (string) ($rendered['text'] ?? ''),
            'inline_css' => true,
        ]);

        // Dispatch the queued job
        dispatch(new SendEmailTemplateJob($log->id));

        return back()->with('success', 'Test email queued for '.$to);
    }

    /**
     * Render the final HTML exactly as it will be sent (repair <img>, attach from docs,
     * inline images to base64, inline CSS). Does not persist any changes or send email.
     */
    public function renderFinalHtml(Request $request, EmailTemplate $emailTemplate)
    {
        $this->authorize('view', $emailTemplate);

        $renderer = app(EmailTemplateRenderer::class);
        $subject = (string) ($request->input('subject') ?? $emailTemplate->subject_template);
        $html = (string) ($request->input('html') ?? $emailTemplate->html_template);
        $text = (string) ($request->input('text') ?? $emailTemplate->text_template);

        // Do not persist tmp images, but allow previewing with them present
        $html = $this->adoptTmpImagesInHtml($emailTemplate, $html, false);

        // Context resolution (same as sendTest)
        $ctx = [];
        if ($id = $request->integer('activity_id')) {
            $activity = Activity::query()->with(['action', 'decision', 'contract.clientCase.client.person', 'contract.account', 'clientCase.client.person'])->find($id);
            if ($activity) {
                $ctx['activity'] = $activity;
                if ($activity->contract && ! isset($ctx['contract'])) {
                    $ctx['contract'] = $activity->contract;
                }
                if ($activity->clientCase && ! isset($ctx['client_case'])) {
                    $ctx['client_case'] = $activity->clientCase;
                }
                if (($ctx['client_case'] ?? null) && ! isset($ctx['client'])) {
                    $ctx['client'] = $ctx['client_case']->client;
                    $ctx['person'] = optional($ctx['client'])->person;
                }
            }
        }
        if ($id = $request->integer('contract_id')) {
            $contract = Contract::query()->with(['clientCase.client.person', 'account'])->find($id);
            if ($contract) {
                $ctx['contract'] = $contract;
                if ($contract->clientCase) {
                    $ctx['client_case'] = $contract->clientCase;
                    if ($contract->clientCase->client) {
                        $ctx['client'] = $contract->clientCase->client;
                        $ctx['person'] = optional($contract->clientCase->client)->person;
                    }
                }
            }
        }
        if (! isset($ctx['client_case']) && ($id = $request->integer('case_id'))) {
            $case = ClientCase::query()->with(['client.person'])->find($id);
            if ($case) {
                $ctx['client_case'] = $case;
                if ($case->client) {
                    $ctx['client'] = $case->client;
                    $ctx['person'] = optional($case->client)->person;
                }
            }
        }
        if (! isset($ctx['client']) && ($id = $request->integer('client_id'))) {
            $client = Client::query()->with(['person'])->find($id);
            if ($client) {
                $ctx['client'] = $client;
                $ctx['person'] = optional($client)->person;
            }
        }
        $ctx['extra'] = (array) $request->input('extra', []);
        $ctx['mail_profile'] = MailProfile::query()->orderBy('active', 'desc')->orderBy('priority')->orderBy('id')->first();

        $rendered = $renderer->render([
            'subject' => $subject,
            'html' => $html,
            'text' => $text,
        ], $ctx);

        $attachments = [];
        if (! empty($rendered['html'])) {
            $rendered['html'] = $this->repairImgWithoutSrc($rendered['html']);
            $rendered['html'] = $this->attachSrcFromTemplateDocuments($emailTemplate, $rendered['html']);
            $embed = (string) $request->input('embed', 'base64');
            if ($embed === 'base64') {
                try {
                    $imageInliner = app(\App\Services\EmailImageInliner::class);
                    $rendered['html'] = $imageInliner->inline($rendered['html']);
                } catch (\Throwable $e) {
                }
            } else {
                $rendered['html'] = $this->absolutizeStorageUrls($request, $rendered['html']);
            }
            try {
                $inliner = new CssToInlineStyles;
                $rendered['html'] = $inliner->convert($rendered['html']);
            } catch (\Throwable $e) {
            }
        }

        return response()->json([
            'subject' => $rendered['subject'] ?? $subject,
            'html' => $rendered['html'] ?? '',
            'text' => $rendered['text'] ?? ($text ?? ''),
            'attachments' => $attachments,
        ]);
    }

    /**
     * Convert any <img src="/storage/..."> (or absolute URLs whose path is /storage/...) to
     * absolute URLs using the current request scheme+host, so email clients like Gmail can fetch
     * them through their proxy reliably.
     */
    protected function absolutizeStorageUrls(Request $request, string $html): string
    {
        if ($html === '' || stripos($html, '<img') === false) {
            return $html;
        }
        $base = (string) (config('app.asset_url') ?: config('app.url'));
        $host = $base !== '' ? rtrim($base, '/') : $request->getSchemeAndHttpHost();

        return preg_replace_callback('#<img([^>]+)src=["\']([^"\']+)["\']([^>]*)>#i', function (array $m) use ($host) {
            $before = $m[1] ?? '';
            $src = $m[2] ?? '';
            $after = $m[3] ?? '';
            $path = $src;
            if (preg_match('#^https?://#i', $src)) {
                $parts = parse_url($src);
                $path = $parts['path'] ?? '';
                if (! preg_match('#^/?storage/#i', (string) $path)) {
                    return $m[0];
                }
            } else {
                if (! preg_match('#^/?storage/#i', (string) $path)) {
                    return $m[0];
                }
            }
            $rel = '/'.ltrim(preg_replace('#^/?storage/#i', 'storage/', (string) $path), '/');
            $abs = rtrim($host, '/').$rel;

            return '<img'.$before.'src="'.$abs.'"'.$after.'>';
        }, $html);
    }

    /**
     * Fix patterns where an <img ...> tag lacks a src attribute but is immediately followed by a URL.
     * Example to fix:
     *   <img alt="Logo">\nhttps://domain.tld/storage/email-images/foo.png
     * becomes:
     *   <img alt="Logo" src="https://domain.tld/storage/email-images/foo.png">
     * The trailing URL text is removed.
     */
    protected function repairImgWithoutSrc(string $html): string
    {
        if ($html === '' || stripos($html, '<img') === false) {
            return $html;
        }

        // Helper to set src on an <img> when not present and keep the in-between content
        $setSrc = function (array $m): string {
            $attrs = $m[1] ?? '';
            $between = $m[2] ?? '';
            $url = $m[3] ?? '';
            if (preg_match('#\bsrc\s*=#i', $attrs)) {
                return $m[0];
            }

            return '<img'.$attrs.' src="'.$url.'">'.$between;
        };

        // Up to 700 chars of any content (non-greedy) between tag and URL
        $gap = '(.{0,700}?)';
        $urlAbs = '(https?://[^\s<>"\']+/storage/[^\s<>"\']+)';
        $urlRel = '(/storage/[^\s<>"\']+)';

        // Case 1: Plain text URL after <img>
        $html = preg_replace_callback('#<img([^>]*)>'.$gap.$urlAbs.'#is', $setSrc, $html);
        $html = preg_replace_callback('#<img([^>]*)>'.$gap.$urlRel.'#is', $setSrc, $html);

        // Case 2: Linked URL after <img> (keep the anchor text, consume the URL into src)
        $setSrcAnchor = function (array $m): string {
            $attrs = $m[1] ?? '';
            $between = $m[2] ?? '';
            $url = $m[3] ?? '';
            $anchor = $m[4] ?? '';
            if (preg_match('#\bsrc\s*=#i', $attrs)) {
                return $m[0];
            }

            // Keep the anchor but its href stays as-is; we only set img src
            return '<img'.$attrs.' src="'.$url.'">'.$between.$anchor;
        };
        $html = preg_replace_callback('#<img([^>]*)>'.$gap.$urlAbs.'(\s*<a[^>]+href=["\'][^"\']+["\'][^>]*>.*?</a>)#is', $setSrcAnchor, $html);
        $html = preg_replace_callback('#<img([^>]*)>'.$gap.$urlRel.'(\s*<a[^>]+href=["\'][^"\']+["\'][^>]*>.*?</a>)#is', $setSrcAnchor, $html);

        // Fallback: if a single image is missing src and there is a single /storage URL anywhere, attach it
        if (preg_match_all('#<img(?![^>]*\bsrc\s*=)[^>]*>#i', $html, $missingImgs) === 1) {
            if (count($missingImgs[0]) === 1) {
                if (preg_match_all('#(?:https?://[^\s<>"\']+)?/storage/[^\s<>"\']+#i', $html, $urls) === 1 && count($urls[0]) === 1) {
                    $onlyUrl = $urls[0][0];
                    $html = preg_replace('#<img((?![^>]*\bsrc\s*=)[^>]*)>#i', '<img$1 src="'.$onlyUrl.'">', $html, 1);
                }
            }
        }

        return $html;
    }

    /**
     * As a conservative fallback, populate missing <img> src attributes using this template's
     * attached image Documents. We try to match by the alt attribute first (e.g., alt="Logo"
     * will match a document named "logo.*"); if there is only one image document, we will use it.
     */
    protected function attachSrcFromTemplateDocuments(EmailTemplate $tpl, string $html): string
    {
        if ($html === '' || stripos($html, '<img') === false) {
            return $html;
        }

        // Collect candidate image docs from relation if loaded, otherwise query
        $docs = $tpl->getRelationValue('documents');
        if ($docs === null) {
            $docs = $tpl->documents()->get(['id', 'name', 'path', 'file_name', 'original_name', 'mime_type']);
        }
        $imageDocs = collect($docs ?: [])->filter(function ($d) {
            $mime = strtolower((string) ($d->mime_type ?? ''));

            return $mime === '' || str_starts_with($mime, 'image/');
        })->values();

        if ($imageDocs->isEmpty()) {
            return $html;
        }

        // Build lookups by basename without extension
        $byStem = [];
        foreach ($imageDocs as $d) {
            $base = pathinfo($d->file_name ?: ($d->name ?: ($d->original_name ?: basename((string) $d->path))), PATHINFO_FILENAME);
            if ($base) {
                $byStem[strtolower($base)] = $d;
            }
        }

        $callback = function (array $m) use (&$byStem, $imageDocs) {
            $attrs = $m[1] ?? '';
            if (preg_match('#\bsrc\s*=#i', $attrs)) {
                return $m[0];
            }

            $alt = null;
            if (preg_match('#\balt\s*=\s*(?:"([^"]*)"|\'([^\']*)\')#i', $attrs, $am)) {
                $alt = trim(html_entity_decode($am[1] !== '' ? $am[1] : ($am[2] ?? ''), ENT_QUOTES | ENT_HTML5));
            }

            $chosen = null;
            if ($alt) {
                $key = strtolower(preg_replace('#[^a-z0-9]+#i', '', $alt));
                // try exact stem
                if (isset($byStem[strtolower($alt)])) {
                    $chosen = $byStem[strtolower($alt)];
                }
                if (! $chosen) {
                    // try relaxed: any stem containing the alt
                    foreach ($byStem as $stem => $doc) {
                        $relaxedStem = preg_replace('#[^a-z0-9]+#i', '', (string) $stem);
                        if ($relaxedStem !== '' && str_contains($relaxedStem, $key)) {
                            $chosen = $doc;
                            break;
                        }
                    }
                }
            }

            if (! $chosen && method_exists($imageDocs, 'count') && $imageDocs->count() === 1) {
                $chosen = $imageDocs->first();
            }

            if (! $chosen) {
                return $m[0];
            }

            $url = '/storage/'.ltrim((string) $chosen->path, '/');

            return '<img'.$attrs.' src="'.$url.'">';
        };

        $html = preg_replace_callback('#<img([^>]*)>#i', $callback, $html);

        return $html;
    }

    /**
     * Upload an image for use in email templates. Stores to a temporary folder first and returns a public URL.
     */
    public function uploadImage(Request $request)
    {
        $this->authorize('create', EmailTemplate::class);

        $validated = $request->validate([
            'file' => ['required', 'image', 'max:5120'], // 5MB
        ]);

        /** @var \Illuminate\Http\UploadedFile $file */
        $file = $validated['file'];
        // store into tmp first; move on save
        $path = $file->store('tmp/email-images', 'public');
        // Return a relative URL to avoid mismatched host/ports in dev
        $url = '/storage/'.$path;

        return response()->json([
            'url' => $url,
            'path' => $path,
            'tmp' => true,
        ]);
    }

    /**
     * Replace an image referenced by the template, updating the existing Document row if found
     * (and deleting the old file), or creating a new Document if none exists. Returns the new URL.
     */
    public function replaceImage(Request $request, EmailTemplate $emailTemplate)
    {
        $this->authorize('update', $emailTemplate);

        $validated = $request->validate([
            'file' => ['required', 'image', 'max:5120'],
            'current_src' => ['nullable', 'string'],
        ]);

        /** @var \Illuminate\Http\UploadedFile $file */
        $file = $validated['file'];
        $currentSrc = (string) ($validated['current_src'] ?? '');

        // Normalize current src to a public disk path when possible
        $currentPath = null;
        if ($currentSrc !== '') {
            $parsed = parse_url($currentSrc);
            $path = $parsed['path'] ?? $currentSrc;
            // Accept /storage/... or raw path; strip leading storage/
            if (preg_match('#/storage/(.+)#i', $path, $m)) {
                $path = $m[1];
            }
            $path = ltrim(preg_replace('#^storage/#', '', $path), '/');
            if ($path !== '') {
                $currentPath = $path;
            }
        }

        // Find existing document for this template matching the path
        $doc = null;
        if ($currentPath) {
            $doc = $emailTemplate->documents()->where('path', $currentPath)->first();
        }

        // Store the new file
        $ext = $file->getClientOriginalExtension();
        $nameBase = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME) ?: 'image';
        $dest = 'email-images/'.$nameBase.'-'.Str::uuid().($ext ? ('.'.$ext) : '');
        Storage::disk('public')->put($dest, File::get($file->getRealPath()));

        // Delete old file if we will update an existing document
        if ($doc && $doc->path && Storage::disk('public')->exists($doc->path)) {
            try {
                Storage::disk('public')->delete($doc->path);
            } catch (\Throwable $e) {
                // ignore
            }
        }

        $full = storage_path('app/public/'.$dest);
        try {
            $mime = File::exists($full) ? File::mimeType($full) : null;
        } catch (\Throwable $e) {
            $mime = null;
        }
        try {
            $size = Storage::disk('public')->size($dest);
        } catch (\Throwable $e) {
            $size = null;
        }

        if ($doc) {
            $doc->forceFill([
                'name' => basename($dest),
                'path' => $dest,
                'file_name' => basename($dest),
                'original_name' => $file->getClientOriginalName(),
                'extension' => $ext ?: null,
                'mime_type' => $mime,
                'size' => $size,
            ])->save();
        } else {
            $doc = $emailTemplate->documents()->create([
                'name' => basename($dest),
                'description' => null,
                'user_id' => optional(auth()->user())->id,
                'disk' => 'public',
                'path' => $dest,
                'file_name' => basename($dest),
                'original_name' => $file->getClientOriginalName(),
                'extension' => $ext ?: null,
                'mime_type' => $mime,
                'size' => $size,
                'is_public' => true,
            ]);
        }

        return response()->json([
            'url' => '/storage/'.$dest,
            'path' => $dest,
            'document_id' => $doc->id,
            'replaced' => (bool) $currentPath,
        ]);
    }

    /**
     * Delete an attached image Document from the given email template.
     */
    public function deleteImage(Request $request, EmailTemplate $emailTemplate, Document $document)
    {
        $this->authorize('update', $emailTemplate);

        // Ensure the document belongs to this template (polymorphic relation)
        if ((int) $document->documentable_id !== (int) $emailTemplate->id || $document->documentable_type !== EmailTemplate::class) {
            return response()->json(['message' => 'Document does not belong to this template.'], 422);
        }

        try {
            // Force delete to remove underlying file as well (Document model handles file deletion on force delete)
            $document->forceDelete();
        } catch (\Throwable $e) {
            return response()->json(['message' => 'Failed to delete image: '.$e->getMessage()], 500);
        }

        return response()->json(['deleted' => true]);
    }

    /**
     * Scan HTML for images stored in /storage/tmp/email-images and move them into a permanent
     * location under /storage/email-images, create Document records and update the HTML.
     */
    protected function adoptTmpImages(EmailTemplate $tpl): void
    {
        $html = (string) ($tpl->html_template ?? '');
        if ($html === '' || stripos($html, 'tmp/email-images/') === false) {
            return;
        }

        // Match any tmp paths inside src attributes, accepting absolute or relative URLs
        $paths = [];
        $matches = [];
        if (preg_match_all('#/storage/tmp/email-images/[^"\']+#i', $html, $matches)) {
            $paths = array_merge($paths, $matches[0]);
        }
        if (preg_match_all('#tmp/email-images/[^"\']+#i', $html, $matches)) {
            $paths = array_merge($paths, $matches[0]);
        }
        $paths = array_values(array_unique($paths));
        if (empty($paths)) {
            return;
        }

        foreach ($paths as $tmpRel) {
            // Normalize path (strip any leading storage/)
            // Normalize to disk-relative path
            $tmpRel = ltrim(preg_replace('#^/?storage/#i', '', $tmpRel), '/');
            if (! Storage::disk('public')->exists($tmpRel)) {
                continue;
            }

            $ext = pathinfo($tmpRel, PATHINFO_EXTENSION);
            $base = pathinfo($tmpRel, PATHINFO_FILENAME);
            $candidate = 'email-images/'.$base.'-'.Str::uuid().($ext ? ('.'.$ext) : '');
            // Ensure dest doesn't exist
            while (Storage::disk('public')->exists($candidate)) {
                $candidate = 'email-images/'.$base.'-'.Str::uuid().($ext ? ('.'.$ext) : '');
            }

            // Move the file
            Storage::disk('public')->move($tmpRel, $candidate);

            // Create Document record
            try {
                $full = storage_path('app/public/'.$candidate);
                $mime = File::exists($full) ? File::mimeType($full) : null;
            } catch (\Throwable $e) {
                $mime = null;
            }
            try {
                $size = Storage::disk('public')->size($candidate);
            } catch (\Throwable $e) {
                $size = null;
            }

            $tpl->documents()->create([
                'name' => basename($candidate),
                'description' => null,
                'user_id' => optional(auth()->user())->id,
                'disk' => 'public',
                'path' => $candidate,
                'file_name' => basename($candidate),
                'original_name' => basename($candidate),
                'extension' => $ext ?: null,
                'mime_type' => $mime,
                'size' => $size,
                'is_public' => true,
            ]);

            // Update HTML to reference the new permanent path (use relative /storage URL)
            $to = '/storage/'.$candidate;
            $from = ['/storage/'.$tmpRel, $tmpRel];
            $html = str_replace($from, $to, $html);
            // Also replace absolute URL variants like https://domain/storage/<path>
            $pattern = '#https?://[^"\']+/storage/'.preg_quote($tmpRel, '#').'#i';
            $html = preg_replace($pattern, $to, $html);
        }
        if ($html !== (string) ($tpl->html_template ?? '')) {
            $tpl->forceFill(['html_template' => $html])->save();
        }
    }

    /**
     * Move any tmp images present in the provided HTML into permanent storage, attach documents,
     * and return the updated HTML. Optionally persist the template's HTML.
     */
    protected function adoptTmpImagesInHtml(EmailTemplate $tpl, string $html, bool $persistTemplate = false): string
    {
        if ($html === '' || stripos($html, 'tmp/email-images/') === false) {
            return $html;
        }
        $paths = [];
        $matches = [];
        if (preg_match_all('#/storage/tmp/email-images/[^"\']+#i', $html, $matches)) {
            $paths = array_merge($paths, $matches[0]);
        }
        if (preg_match_all('#tmp/email-images/[^"\']+#i', $html, $matches)) {
            $paths = array_merge($paths, $matches[0]);
        }
        $paths = array_values(array_unique($paths));
        if (empty($paths)) {
            return $html;
        }
        foreach ($paths as $tmpRel) {
            $tmpRel = ltrim(preg_replace('#^/?storage/#i', '', $tmpRel), '/');
            if (! Storage::disk('public')->exists($tmpRel)) {
                continue;
            }
            $ext = pathinfo($tmpRel, PATHINFO_EXTENSION);
            $base = pathinfo($tmpRel, PATHINFO_FILENAME);
            $candidate = 'email-images/'.$base.'-'.Str::uuid().($ext ? ('.'.$ext) : '');
            while (Storage::disk('public')->exists($candidate)) {
                $candidate = 'email-images/'.$base.'-'.Str::uuid().($ext ? ('.'.$ext) : '');
            }
            Storage::disk('public')->move($tmpRel, $candidate);
            try {
                $mime = File::exists(storage_path('app/public/'.$candidate)) ? File::mimeType(storage_path('app/public/'.$candidate)) : null;
            } catch (\Throwable $e) {
                $mime = null;
            }
            try {
                $size = Storage::disk('public')->size($candidate);
            } catch (\Throwable $e) {
                $size = null;
            }

            $tpl->documents()->create([
                'name' => basename($candidate),
                'description' => null,
                'user_id' => optional(auth()->user())->id,
                'disk' => 'public',
                'path' => $candidate,
                'file_name' => basename($candidate),
                'original_name' => basename($candidate),
                'extension' => $ext ?: null,
                'mime_type' => $mime,
                'size' => $size,
                'is_public' => true,
            ]);
            $to = '/storage/'.$candidate;
            $from = ['/storage/'.$tmpRel, $tmpRel];
            $html = str_replace($from, $to, $html);
            $pattern = '#https?://[^"\']+/storage/'.preg_quote($tmpRel, '#').'#i';
            $html = preg_replace($pattern, $to, $html);
        }
        if ($persistTemplate && $tpl->exists) {
            $tpl->forceFill(['html_template' => $html])->save();
        }

        return $html;
    }

    /**
     * Small JSON endpoints to support cascading selects in editor preview.
     */
    public function clients(Request $request)
    {
        $this->authorize('viewAny', EmailTemplate::class);
        $items = Client::query()->with(['person'])->latest('id')->limit(50)->get();

        return response()->json($items->map(fn ($c) => [
            'id' => $c->id,
            'label' => trim(($c->person->full_name ?? '').' #'.$c->id) ?: ('Client #'.$c->id),
        ]));
    }

    public function casesForClient(Request $request, Client $client)
    {
        $this->authorize('viewAny', EmailTemplate::class);
        $items = ClientCase::query()
            ->with(['person'])
            ->where('client_id', $client->id)
            ->latest('id')
            ->limit(50)
            ->get();

        return response()->json($items->map(function ($cs) {
            $person = $cs->person->full_name ?? '';
            $ref = $cs->reference ?? '';
            $base = trim(($ref !== '' ? ($ref.' ') : '').'#'.$cs->id);
            $label = trim(($person !== '' ? ($person.' — ') : '').$base);

            return [
                'id' => $cs->id,
                'label' => $label !== '' ? $label : ('Case #'.$cs->id),
            ];
        }));
    }

    public function contractsForCase(Request $request, ClientCase $clientCase)
    {
        $this->authorize('viewAny', EmailTemplate::class);
        $items = Contract::query()->where('client_case_id', $clientCase->id)->latest('id')->limit(50)->get();

        return response()->json($items->map(fn ($ct) => [
            'id' => $ct->id,
            'label' => trim(($ct->reference ?? '').' #'.$ct->id) ?: ('Contract #'.$ct->id),
        ]));
    }
}
