<?php

namespace App\Services;

use App\Facades\PrintQueue;
use App\Jobs\SendEmailJob;
use App\Models\Client\Client;
use App\Models\Contact\Send\EmailSend;
use App\Models\Document\DocumentType;
use App\Models\Dsar\Dsar;
use App\Models\Dsar\DsarType;
use App\Support\SendStatus;
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 PlaceholderService $placeholders,
        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');

        // Store the storage path only - base64 encoding is deferred to the job
        // so we never hold the full PDF in memory longer than the HTTP call itself.
        $attachments = [];
        if (Storage::exists($path)) {
            $attachments[] = [
                'name' => 'DSAR-' . strtoupper(substr($dsar->slug, 0, 8)) . '.pdf',
                'mime' => 'application/pdf',
                'path' => $path,
            ];
        }

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

        $emailSend = EmailSend::create([
            'sendable_id'   => $dsar->id,
            'sendable_type' => Dsar::class,
            'to_email'      => (string) $contact->email,
            'to_name'       => $contact->name,
            'from_email'    => $fromEmail,
            'from_name'     => $fromName,
            'subject'       => 'Data Subject Access Request - ' . $client->fullName,
            'html_body'     => $body,
            'attachments'   => $attachments,
            'status'        => SendStatus::PENDING,
        ]);

        SendEmailJob::dispatch($emailSend);
    }

    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) {

                $chromeBase = storage_path('app/chrome');

                $browsershot
                    ->setChromePath('/usr/bin/chrome-puppeteer')
                    ->noSandbox()
                    ->setUserDataDir($chromeBase . '/data')
                    ->setEnvironmentOptions([
                        'HOME' => $chromeBase,
                        'XDG_CONFIG_HOME' => $chromeBase . '/config',
                        'XDG_CACHE_HOME' => $chromeBase . '/cache',
                        'CHROME_CONFIG_HOME' => $chromeBase . '/config',
                    ])
                    ->addChromiumArguments([
                        '--disable-dev-shm-usage',
                        '--disable-gpu',
                        '--headless=new',
                    ])
                    ->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');

        $typeIds = DocumentType::whereIn('display_name', ['ID', 'LOA'])->pluck('id');

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

        $documents = $dsar->claim->client->documents()->whereIn('type_id', $typeIds)->get();

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

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

        try {
            $attachPaths = [];

            foreach ($documents as $doc) {
                $storagePath  = $claim->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');

            $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, array $extra = []): string {
        $placeholders = $this->placeholders->forDsar($type, $client, $extra);
        return $this->placeholders->replace($type->content, $placeholders);
    }
}
