<?php

namespace App\Services;

use App\Enum\Contact\MessageChannel;
use App\Enum\Contact\MessageDirection;
use App\Enum\Contact\MessageProvider;
use App\Enum\Contact\MessageStatus;
use App\Facades\PrintQueue;
use App\Jobs\Contact\SendContactMessageJob;
use App\Models\Client\Client;
use App\Models\Contact\Hub\ContactMessage;
use App\Models\Contact\Hub\ContactMessageAttachment;
use App\Models\Dsar\Dsar;
use App\Models\Dsar\DsarType;
use App\Models\Institution\Institution;
use App\Models\User\User;
use App\Services\Communications\Placeholders\TemplateRenderer;
use App\ValueObjects\Communications\PlaceholderContext;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Spatie\Browsershot\Browsershot;
use Spatie\LaravelPdf\Facades\Pdf;

class DsarService {

    public function __construct(
        private TemplateRenderer $renderer,
        private HistoryService $history,
        private PdfService $pdfService,
    ) {}

    public function send(Dsar $dsar): void {
        $dsar->load(['institution.contacts', 'claim.client', 'solicitor']);

        try {
            $path = $this->buildPdf($dsar);
            $dsar->update(['file_path' => basename($path)]);

            $sendType = $dsar->institution?->send_type;

            $client = $dsar->claim->client;

            if (in_array($sendType, ['email', 'both'])) {
                $this->queueEmail($dsar, $path);
                $dsar->update(['status' => 'sent']);
                $context = [
                    'dsar_id' => $dsar->id,
                    'claim_id' => $dsar->claim_id,
                    'institution' => $dsar->institution->display_name,
                ];
                $this->history->add($dsar, 'DSAR to "' . $dsar->institution->display_name . '" queued for email delivery', $context);
                $this->history->add($client, 'DSAR to "' . $dsar->institution->display_name . '" queued for email delivery', $context);
            }

            if (in_array($sendType, ['post', 'both'])) {
                PrintQueue::addPath($path, $dsar);
                if ($sendType === 'post') {
                    $dsar->update(['status' => 'needs posting']);
                }
                $context = [
                    'dsar_id' => $dsar->id,
                    'claim_id' => $dsar->claim_id,
                    'institution' => $dsar->institution->display_name,
                ];
                $this->history->add($dsar, 'DSAR to "' . $dsar->institution->display_name . '" added to print queue', $context);
                $this->history->add($client, 'DSAR to "' . $dsar->institution->display_name . '" added to print queue', $context);
            }

            if (!in_array($sendType, ['email', 'post', 'both'])) {
                $dsar->update(['status' => 'sent']);
            }

        } catch (\Exception|\Error $e) {
            Log::error($e);
            $dsar->update(['status' => 'failed']);
        }
    }

    private function queueEmail(Dsar $dsar, string $path): void {
        $institution = $dsar->institution;

        $contact = $institution->contacts->firstWhere('is_primary', true)
            ?? $institution->contacts->first();

        if (!$contact?->email) {
            return;
        }

        $client = $dsar->claim->client;

        $sender    = $dsar->solicitor?->emailSenderFor('dsar');
        $fromEmail = $sender?->email_address ?? config('mail.from.address');
        $fromName  = $sender?->display_name  ?? config('mail.from.name');

        $signature = $sender?->signature ?? '';
        $body      = $dsar->email_content ?? $dsar->content;
        if (!empty(trim(strip_tags($signature)))) {
            $body .= '<br><br>' . $signature;
        }

        $contactMessage = ContactMessage::create([
            'solicitor_id'     => $dsar->solicitor?->id,
            'channel'          => MessageChannel::EMAIL,
            'direction'        => MessageDirection::OUTBOUND,
            'provider'         => MessageProvider::SENDGRID,
            'status'           => MessageStatus::QUEUED,
            'messageable_type' => Dsar::class,
            'messageable_id'   => $dsar->id,
            'sender_email_id'  => $sender?->id,
            'recipient_email'  => (string) $contact->email,
            'recipient_name'   => $contact->name,
            'sender_value'     => $fromEmail,
            'sender_display'   => $fromName,
            'subject'          => 'Data Subject Access Request - ' . $client->displayId
                                . ($dsar->reference ? ' [Ref: ' . $dsar->reference . ']' : ''),
            'body_html'        => $body,
        ]);

        // Attach the DSAR PDF
        if (Storage::exists($path)) {
            ContactMessageAttachment::create([
                'contact_message_id' => $contactMessage->id,
                'disk'               => config('filesystems.default'),
                'path'               => $path,
                'filename'           => 'DSAR-' . strtoupper(substr($dsar->slug, 0, 8)) . '.pdf',
                'mime'               => 'application/pdf',
                'size'               => Storage::size($path),
            ]);
        }

        // Attach the latest LOA and POA documents for the client (type IDs 2, 5)
        $clientDocs = $client->documents()
            ->whereIn('type_id', [2, 5])
            ->orderByDesc('created_at')
            ->get()
            ->unique('type_id');

        foreach ($clientDocs as $doc) {
            $docPath = $client->getDocumentStoragePath() . $doc->file_name;
            if (Storage::disk('public')->exists($docPath)) {
                ContactMessageAttachment::create([
                    'contact_message_id' => $contactMessage->id,
                    'disk'               => 'public',
                    'path'               => $docPath,
                    'filename'           => $doc->file_name,
                    'mime'               => $doc->file_type ?? 'application/octet-stream',
                    'size'               => Storage::disk('public')->size($docPath),
                ]);
            }
        }

        SendContactMessageJob::dispatch($contactMessage->id);
    }

    public function buildPdf(Dsar $dsar): string {
        $dsar->loadMissing(['claim.client', 'solicitor', 'institution']);

        $path = 'pdfs/dsar';

        if (!Storage::exists($path)) {
            Storage::makeDirectory($path);
        }

        $fileName  = $path . '/' . $dsar->slug . '.pdf';
        $solicitor = $dsar->solicitor;
        $solicitorAddress = $solicitor
            ? ($solicitor->addresses()->where('is_current', true)->first()
                ?? $solicitor->addresses()->latest()->first())
            : null;
        $logoUrl = null;
        if ($solicitor?->logo_path && Storage::exists($solicitor->logo_path)) {
            $logoUrl = 'data:' . Storage::mimeType($solicitor->logo_path) . ';base64,'
                . base64_encode(Storage::get($solicitor->logo_path));
        }

        $institution = $dsar->institution;
        $institutionAddress = $institution
            ? ($institution->addresses()->where('is_current', true)->first()
                ?? $institution->addresses()->latest()->first())
            : null;

        $footerHtml = $this->buildFooterHtml($solicitor, $solicitorAddress);

        Pdf::view('pdfs.dsar', [
            'dsar'                => $dsar,
            'solicitor'           => $solicitor,
            'solicitorAddress'    => $solicitorAddress,
            'solicitorLogoUrl'    => $logoUrl,
            'solicitorCompanyNum' => $solicitor?->company_number,
            'solicitorSraNum'     => $solicitor?->sra_number,
            'institution'         => $institution,
            'institutionAddress'  => $institutionAddress,
        ])
            ->margins(40, 40, 40, 40)
            ->withBrowsershot(function (Browsershot $browsershot) use ($footerHtml) {
                $this->pdfService->configureBrowsershot($browsershot);
                $browsershot
                    ->showBrowserHeaderAndFooter()
                    ->headerHtml('<span></span>')
                    ->footerHtml($footerHtml);
            })
            ->save(Storage::path($fileName));

        // Append any ID / LOA documents from the claim to the DSAR PDF
        $this->appendClaimAttachments($dsar, $fileName);

        return $fileName;
    }

    /**
     * Build the HTML string that Browsershot injects as Chrome's native page footer.
     *
     * Chrome renders this template in the bottom-margin zone on every page. External
     * stylesheets and class-based CSS do not apply here — everything must be inline.
     * Font sizes must be in px; mm/pt are supported but em/rem are not reliable.
     *
     * The footer is horizontally padded to align with the main content's left/right
     * margins (40 mm each side on a 210 mm A4 sheet ≈ 19%).
     */
    private function buildFooterHtml(?object $solicitor, ?object $solicitorAddress): string
    {
        $parts = [];

        if ($solicitorAddress) {
            $regOffice = collect([
                $solicitorAddress->line_1,
                $solicitorAddress->line_2,
                $solicitorAddress->line_3,
                $solicitorAddress->line_4,
                $solicitorAddress->postcode,
            ])->filter()->implode(', ');

            if ($regOffice) {
                $parts[] = 'Registered Office: ' . e($regOffice);
            }
        }

        if ($solicitor?->company_number) {
            array_unshift($parts,
                e($solicitor->name ?? 'This firm')
                . ', a company registered in England and Wales with registered number '
                . e($solicitor->company_number)
            );
        }

        if ($solicitor?->sra_number) {
            // Insert after the company line (index 1) if it exists, else append
            $sraLine = 'Authorised and regulated by the Solicitors Regulation Authority number '
                . e($solicitor->sra_number);

            if (count($parts) >= 1 && $solicitor?->company_number) {
                array_splice($parts, 1, 0, [$sraLine]);
            } else {
                array_unshift($parts, $sraLine);
            }
        }

        if (empty($parts)) {
            $parts[] = 'This letter is confidential and intended solely for the named recipient.';
        }

        $lines = implode('<br>', $parts);

        // All styles inline — Chrome footer templates do not inherit the page's CSS.
        return '<div style="'
            . 'box-sizing:border-box;'
            . 'width:100%;'
            . 'padding:4px 40mm 0 40mm;'   // align with 40mm page margins
            . 'font-family:Arial,Helvetica,sans-serif;'
            . 'font-size:7.5pt;'
            . 'color:#777777;'
            . 'text-align:center;'
            . 'line-height:1.55;'
            . 'border-top:0.75px solid #cccccc;'
            . '">'
            . $lines
            . '</div>';
    }

    /**
     * Find ID and LOA documents attached to the claim and merge them onto the end of the
     * generated DSAR PDF.  PDFs are appended directly; images are first wrapped in a
     * single-page PDF via Browsershot.
     */
    private function appendClaimAttachments(Dsar $dsar, string $dsarPdfPath): void {
        $dsar->loadMissing('claim');

        $documents = $dsar->claim->client->documents()
            ->with('owner')
            ->whereIn('type_id', [2, 5])
            ->orderByDesc('created_at')
            ->get()
            ->unique('type_id');

        if ($documents->isEmpty()) {
            return;
        }

        $tempPdfs = [];   // image-to-PDF temp paths to clean up afterwards

        try {
            $attachPaths = [];

            foreach ($documents as $doc) {
                $storagePath  = $doc->owner->getDocumentStoragePath() . $doc->file_name;
                $absolutePath = Storage::disk('public')->path($storagePath);

                if (!file_exists($absolutePath)) {
                    continue;
                }

                $fileType = strtolower($doc->file_type ?? '');

                if (str_contains($fileType, 'pdf')) {
                    $attachPaths[] = $absolutePath;
                } elseif (str_starts_with($fileType, 'image/')) {
                    $tempStoragePath = $this->pdfService->imageToPdf($absolutePath, $doc->file_type);
                    $tempPdfs[]      = $tempStoragePath;
                    $attachPaths[]   = Storage::path($tempStoragePath);
                }
            }

            if (empty($attachPaths)) {
                return;
            }

            // Merge: DSAR letter + attachments → temp → replace original
            $merged = $this->pdfService->temp('dsar-merged');

            Log::debug($merged);

            $this->pdfService->mergeAbsolute(
                [Storage::path($dsarPdfPath), ...$attachPaths],
                $merged,
            );

            // Replace the original DSAR PDF with the merged version
            Storage::put($dsarPdfPath, Storage::get($merged));
            Storage::delete($merged);

        } finally {
//            foreach ($tempPdfs as $tmp) {
//                Storage::delete($tmp);
//            }
        }
    }

    public function buildContent(DsarType $type, Client $client, Institution $institution, array $extra = [], ?User $user = null): string {
        [$rendered] = $this->renderer->renderRaw($type->content, $this->contextFor($client, $institution, $extra, $user), style: 'percent');
        return $rendered ?? '';
    }

    public function buildEmailContent(DsarType $type, Client $client, Institution $institution, array $extra = [], ?User $user = null): ?string {
        if (!$type->email_content) {
            return null;
        }
        [$rendered] = $this->renderer->renderRaw($type->email_content, $this->contextFor($client, $institution, $extra, $user), style: 'percent');
        return $rendered;
    }

    /**
     * Re-render a draft DSAR's stored content/email_content from its source template.
     *
     * The content is baked in at creation time, so a change to a placeholder resolver
     * (e.g. the solicitor DSAR sender email/phone) only reaches existing drafts by
     * re-rendering them here. The DSAR's own author (user_id) and reference are fed
     * back in so those placeholders resolve to the same values as at creation, rather
     * than to the current request's auth user / a fresh reference.
     *
     * Returns false when the DSAR can't be re-rendered (no institution DSAR template).
     */
    public function rebuildContent(Dsar $dsar): bool {
        $dsar->loadMissing(['claim.client', 'institution.dsarType', 'user']);

        $client      = $dsar->claim?->client;
        $institution = $dsar->institution;
        $type        = $institution?->dsarType;

        if (!$client || !$institution || !$type) {
            return false;
        }

        $extra = ['dsar_reference' => $dsar->reference];

        $dsar->update([
            'content'       => $this->buildContent($type, $client, $institution, $extra, $dsar->user),
            'email_content' => $this->buildEmailContent($type, $client, $institution, $extra, $dsar->user),
        ]);

        return true;
    }

    private function contextFor(Client $client, Institution $institution, array $extra, ?User $user = null): PlaceholderContext {
        return PlaceholderContext::fromMessageable(
            messageable: $client,
            related: $institution,
            solicitor: $client->solicitor,
            user: $user ?? auth()->user(),
            extra: $extra,
        );
    }
}
