<?php

namespace App\Services;

use App\Mail\MembershipCertificateMail;
use App\Models\Member;
use App\Models\MembershipCertificate;
use App\Models\MembershipRenewalConfig;
use App\Models\Organization;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Barryvdh\DomPDF\Facade\Pdf;

class MembershipCertificateService
{
    /**
     * Generate a membership certificate for a member.
     * Marks any existing current certificate as not current,
     * creates the PDF, stores it, creates the DB record,
     * updates the member's membership_end_date, and sends the email.
     */
    public function generateForMember(Member $member): MembershipCertificate
    {
        $months = $this->getRenewalPeriodMonths($member);
        $startDate = $member->membership_start_date ?? now()->toDateString();
        $validTillDate = $this->calculateValidTillDate(Carbon::parse($startDate), $months);

        // Mark existing current certificates as not current
        $member->certificates()->where('is_current', true)->update(['is_current' => false]);

        // Generate certificate number
        $certNumber = MembershipCertificate::generateCertificateNumber();

        // Build PDF
        $pdfPath = $this->buildAndStorePdf($member, $certNumber, $startDate, $validTillDate);

        // Create DB record
        $certificate = MembershipCertificate::create([
            'member_id'          => $member->id,
            'certificate_number' => $certNumber,
            'issue_date'         => now()->toDateString(),
            'valid_till_date'    => $validTillDate->toDateString(),
            'pdf_path'           => $pdfPath,
            'is_current'         => true,
            'issued_by'          => auth()->id(),
        ]);

        // Update member's membership_end_date
        $member->update(['membership_end_date' => $validTillDate->toDateString()]);

        // Send email — failure must not bubble up
        try {
            $this->sendCertificateEmail($certificate);
        } catch (\Throwable $e) {
            Log::warning('MembershipCertificateService: email delivery failed', [
                'member_id'      => $member->id,
                'certificate_id' => $certificate->id,
                'error'          => $e->getMessage(),
            ]);
        }

        return $certificate;
    }

    /**
     * Regenerate a certificate for a member (deletes old PDF first).
     */
    public function regenerateForMember(Member $member): MembershipCertificate
    {
        // Delete old PDF file if it exists
        $current = $member->currentCertificate();
        if ($current && $current->pdf_path) {
            Storage::disk('public')->delete($current->pdf_path);
        }

        return $this->generateForMember($member);
    }

    /**
     * Send the certificate email to the member.
     */
    public function sendCertificateEmail(MembershipCertificate $certificate): bool
    {
        $member = $certificate->member;
        $email  = $member->email;

        if (!$email || $email === 'N/A') {
            Log::warning('MembershipCertificateService: member has no valid email', [
                'member_id' => $member->id,
            ]);
            return false;
        }

        Mail::to($email)->send(new MembershipCertificateMail($certificate));

        return true;
    }

    /**
     * Get the renewal period in months for a member based on their primary category.
     */
    public function getRenewalPeriodMonths(Member $member): int
    {
        $category = $member->primary_category;
        $config   = MembershipRenewalConfig::getForCategory($category?->id);

        return $config->renewal_period_months ?? 12;
    }

    /**
     * Calculate the valid-till date by adding months to a start date.
     */
    public function calculateValidTillDate(Carbon $startDate, int $months): Carbon
    {
        return $startDate->copy()->addMonths($months);
    }

    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------

    /**
     * Build the PDF and store it; returns the storage-relative path.
     */
    private function buildAndStorePdf(
        Member $member,
        string $certNumber,
        $startDate,
        Carbon $validTillDate
    ): string {
        $organization = Organization::first();
        $orgName      = $organization?->name ?? config('app.name');
        $category     = $member->primary_category;

        $logoBase64 = $this->getLogoBase64();

        $html = view('membership.certificates.pdf', [
            'memberName'        => $member->full_name,
            'organizationName'  => $orgName,
            'categoryName'      => $category?->name,
            'startDate'         => Carbon::parse($startDate)->format('F j, Y'),
            'validTillDate'     => $validTillDate->format('F j, Y'),
            'certificateNumber' => $certNumber,
            'logoBase64'        => $logoBase64,
        ])->render();

        $pdf = Pdf::loadHTML($html)->setPaper('a5', 'landscape');

        $relativePath = 'membership-certificates/' . $certNumber . '.pdf';
        Storage::disk('public')->put($relativePath, $pdf->output());

        return $relativePath;
    }

    /**
     * Embed the logo as a base64 data URI for DomPDF compatibility.
     * Returns null if the file does not exist (graceful degradation).
     */
    private function getLogoBase64(): ?string
    {
        $logoPath = public_path('assets/img/logo.png');

        if (!file_exists($logoPath)) {
            return null;
        }

        try {
            $data = file_get_contents($logoPath);
            $mime = mime_content_type($logoPath) ?: 'image/png';
            return 'data:' . $mime . ';base64,' . base64_encode($data);
        } catch (\Throwable $e) {
            Log::warning('MembershipCertificateService: could not read logo', [
                'path'  => $logoPath,
                'error' => $e->getMessage(),
            ]);
            return null;
        }
    }
}
