<?php

namespace App\Mail;

use App\Enums\FormType;
use App\Models\AdoptionApplication;
use App\Models\Document;
use App\Services\DocumentTemplateService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Shared\Html;

class AdminApprovedAdoptionNotification extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     */
    public function __construct(public AdoptionApplication $application) {}

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        $petName = $this->application->pet?->name ?? 'Unknown Pet'; // @phpstan-ignore nullsafe.neverNull (pet FK nullable)
        $applicantName = $this->application->user->name;

        return new Envelope(
            subject: "*** APPROVED *** Adoption Application - {$petName} for {$applicantName}",
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            markdown: 'emails.admin-approved-adoption-notification',
            with: [
                'application' => $this->application,
                'pet' => $this->application->pet,
                'secondPet' => $this->application->secondPet,
                'applicant' => $this->application->user,
                'adminUrl' => route('filament.admin.resources.adoption-applications.edit', ['record' => $this->application]),
            ],
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, Attachment>
     */
    public function attachments(): array
    {
        $document = Document::forType(FormType::Adoption);

        if (! $document) {
            return [];
        }

        $docxPath = $this->generateDocx($document, $this->application);

        if (! $docxPath || ! file_exists($docxPath)) {
            return [];
        }

        $filename = $this->generateFilename();

        return [
            Attachment::fromPath($docxPath)
                ->as($filename)
                ->withMime('application/vnd.openxmlformats-officedocument.wordprocessingml.document'),
        ];
    }

    /**
     * Generate the .docx file and return the path.
     */
    protected function generateDocx(Document $document, AdoptionApplication $application): ?string
    {
        $templateService = app(DocumentTemplateService::class);

        $renderedData = $templateService->render($document->content, $application);
        $renderedContent = $renderedData['html'];

        $cleanHtml = $this->cleanHtmlForPhpWord($renderedContent);

        $phpWord = new PhpWord;

        $phpWord->setDefaultFontName('Calibri');
        $phpWord->setDefaultFontSize(11);

        $phpWord->addTitleStyle(1, [
            'bold' => true,
            'size' => 24,
            'color' => '000000',
        ], [
            'spaceBefore' => 240,
            'spaceAfter' => 120,
        ]);

        $phpWord->addTitleStyle(2, [
            'bold' => true,
            'size' => 18,
            'color' => '000000',
        ], [
            'spaceBefore' => 240,
            'spaceAfter' => 120,
        ]);

        $section = $phpWord->addSection([
            'marginTop' => 1000,
            'marginBottom' => 1000,
            'marginLeft' => 1000,
            'marginRight' => 1000,
        ]);

        try {
            Html::addHtml($section, $cleanHtml, false, false);
        } catch (\Exception $e) {
            $section->addText('Error parsing document content: '.$e->getMessage());
        }

        $tempFile = tempnam(sys_get_temp_dir(), 'adoption_approval_');
        $tempFile .= '.docx';

        try {
            $writer = IOFactory::createWriter($phpWord, 'Word2007');
            $writer->save($tempFile);

            return $tempFile;
        } catch (\Exception $e) {
            Log::error('Failed to generate approved adoption notification attachment.', [
                'exception' => $e,
                'application_id' => $application->id,
            ]);

            if (file_exists($tempFile)) {
                unlink($tempFile);
            }

            return null;
        }
    }

    /**
     * Clean HTML to be compatible with PHPWord's XML parser.
     */
    protected function cleanHtmlForPhpWord(string $html): string
    {
        // Remove <hr> tags
        $html = preg_replace('/<hr\s*\/?>/i', '', $html) ?? $html;

        // Remove <br> tags
        $html = preg_replace('/<br\s*\/?>/i', '', $html) ?? $html;

        // Convert empty paragraphs to paragraphs with non-breaking space
        $html = preg_replace('/<p>\s*<\/p>/i', '<p>&nbsp;</p>', $html) ?? $html;

        // Convert 'text-align: end' to 'text-align: right'
        $html = str_replace('text-align: end', 'text-align: right', $html);

        // Ensure proper HTML entities
        $html = str_replace('&nbsp;', ' ', $html);

        // Remove problematic tags
        $html = preg_replace('/<script[^>]*>.*?<\/script>/si', '', $html) ?? $html;
        $html = preg_replace('/<style[^>]*>.*?<\/style>/si', '', $html) ?? $html;
        $html = preg_replace('/<iframe[^>]*>.*?<\/iframe>/si', '', $html) ?? $html;

        // Use DOMDocument to ensure valid, well-formed HTML
        $dom = new \DOMDocument('1.0', 'UTF-8');
        $dom->formatOutput = true;

        libxml_use_internal_errors(true);

        $wrappedHtml = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>'.$html.'</body></html>';
        $dom->loadHTML($wrappedHtml, LIBXML_HTML_NODEFDTD | LIBXML_NOERROR | LIBXML_NOWARNING);
        libxml_clear_errors();

        $body = $dom->getElementsByTagName('body')->item(0);
        if ($body && $body->hasChildNodes()) {
            $cleanedHtml = '';
            foreach ($body->childNodes as $child) {
                try {
                    $cleanedHtml .= $dom->saveHTML($child);
                } catch (\Exception) {
                    continue;
                }
            }

            $cleanedHtml = preg_replace('/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;)/i', '&amp;', $cleanedHtml) ?? $cleanedHtml;
            $cleanedHtml = preg_replace('/<(img|br|hr|input|meta|link)([^>]*[^\/])>/i', '<$1$2 />', $cleanedHtml) ?? $cleanedHtml;

            return $cleanedHtml;
        }

        return $html;
    }

    /**
     * Generate the filename for the .docx attachment.
     */
    protected function generateFilename(): string
    {
        $petName = $this->application->pet?->name ?? 'unknown'; // @phpstan-ignore nullsafe.neverNull (pet FK nullable)
        $petName = preg_replace('/[^a-zA-Z0-9_-]/', '_', $petName);

        return sprintf(
            'adoption-agreement-%s-%d-%s.docx',
            $petName,
            $this->application->id,
            now()->format('Y-m-d')
        );
    }
}
