<?php

namespace App\Observers;

use App\Enums\DeclarationStatus;
use App\Mail\DeclarationFileRequestMail;
use App\Models\Declaration;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\ValidationException;

class DeclarationObserver
{
    /**
     * When true, the observer skips sending the client email on en_attente_client.
     * Set this before updating status when the caller already sends the email.
     */
    public static bool $suppressEmail = false;

    /**
     * Handle the Declaration "updating" event.
     *
     * Validates status transitions and auto-archives when status becomes "ferme".
     */
    public function updating(Declaration $declaration): void
    {
        if (! $declaration->isDirty('status')) {
            return;
        }

        $oldStatus = $declaration->getOriginal('status');
        $newStatus = $declaration->status;

        // Handle both string and enum values
        $oldValue = $oldStatus instanceof DeclarationStatus ? $oldStatus->value : (string) $oldStatus;
        $newValue = $newStatus instanceof DeclarationStatus ? $newStatus->value : (string) $newStatus;

        $allowed = DeclarationStatus::allowedTransitions()[$oldValue] ?? [];

        if (! in_array($newValue, $allowed)) {
            throw ValidationException::withMessages([
                'status' => "Invalid status transition from '{$oldValue}' to '{$newValue}'.",
            ]);
        }

        // Auto-archive when status becomes "ferme"
        if ($newValue === DeclarationStatus::Ferme) {
            $declaration->archived_at = now();
        }
    }

    /**
     * Handle the Declaration "updated" event.
     *
     * Dispatches side effects on status transitions:
     * - en_attente_client: queues client email via DeclarationFileRequestMail
     * - ferme: invalidates dashboard cache for all workspace users
     */
    public function updated(Declaration $declaration): void
    {
        if (! $declaration->wasChanged('status')) {
            return;
        }

        $newStatus = $declaration->status instanceof DeclarationStatus
            ? $declaration->status->value
            : (string) $declaration->status;

        if ($newStatus === DeclarationStatus::EnAttenteClient && ! static::$suppressEmail) {
            $this->sendClientFileRequestEmail($declaration);
        }

        // Reset suppression flag after each update
        static::$suppressEmail = false;

        if ($newStatus === DeclarationStatus::Ferme) {
            $this->invalidateDashboardCache($declaration);
        }
    }

    private function sendClientFileRequestEmail(Declaration $declaration): void
    {
        $declaration->loadMissing('client');
        $client = $declaration->client;

        if (! $client || ! $client->contact_email) {
            return;
        }

        $mailJob = DB::transaction(function () use ($declaration, $client) {
            $invitation = $declaration->invitations()
                ->whereNull('used_at')
                ->latest()
                ->first();

            if ($invitation && $invitation->isValid()) {
                // Reuse valid unused invitation, but sync email with current client
                if ($invitation->email !== $client->contact_email) {
                    $invitation->update(['email' => $client->contact_email]);
                    $invitation->refresh();
                }
            } elseif ($invitation && ! $invitation->isValid()) {
                // Expired but unused — renew it
                $invitation->update([
                    'email' => $client->contact_email,
                    'expires_at' => now()->addDays(30),
                ]);
                $invitation->refresh();
            } else {
                // No unused invitation exists (all used or none) — create fresh
                $invitation = $declaration->invitations()->create([
                    'email' => $client->contact_email,
                    'expires_at' => now()->addDays(30),
                ]);
            }

            $body = 'Nous vous invitons à déposer les documents complémentaires pour votre déclaration "'
                . $declaration->title . '".';

            return [
                'email' => $client->contact_email,
                'mailable' => new DeclarationFileRequestMail($declaration, $invitation, $body),
            ];
        });

        Mail::to($mailJob['email'])->queue($mailJob['mailable']);
    }

    private function invalidateDashboardCache(Declaration $declaration): void
    {
        $declaration->loadMissing('workspace.users');
        $workspace = $declaration->workspace;

        if (! $workspace) {
            return;
        }

        $workspace->users->each(function ($user) use ($workspace) {
            Cache::forget("dashboard:{$workspace->id}:{$user->id}");
        });
    }
}
