<?php

namespace App\Services;

use App\Models\AdditionalActivity;
use App\Models\AdditionalActivityTranslation;
use App\Models\Form;
use App\Models\FormNotificationTranslation;
use App\Models\FormTranslation;
use App\Models\Page;
use App\Models\PageTranslationSubmission;
use App\Models\Program;
use App\Models\ProgramTranslation;
use App\Models\Testimonial;
use App\Models\TestimonialTranslation;
use App\Models\TranslatedPage;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class ArchivableRelationsCascadeService
{
    /**
     * Parent models and their soft-deletable child relations.
     *
     * @var array<class-string<Model>, list<array{model: class-string<Model>, foreignKey: string}>>
     */
    private const CHILD_RELATIONS = [
        Program::class => [
            ['model' => ProgramTranslation::class, 'foreignKey' => 'program_id'],
        ],
        AdditionalActivity::class => [
            ['model' => AdditionalActivityTranslation::class, 'foreignKey' => 'additional_activity_id'],
        ],
        Testimonial::class => [
            ['model' => TestimonialTranslation::class, 'foreignKey' => 'testimonial_id'],
        ],
        Form::class => [
            ['model' => FormTranslation::class, 'foreignKey' => 'form_id'],
        ],
        Page::class => [
            ['model' => TranslatedPage::class, 'foreignKey' => 'page_id'],
            ['model' => PageTranslationSubmission::class, 'foreignKey' => 'page_id'],
        ],
    ];

    public function supports(string $modelClass): bool
    {
        return isset(self::CHILD_RELATIONS[$modelClass]);
    }

    public function softDeleteChildren(Model $parent): void
    {
        if ($parent->isForceDeleting()) {
            return;
        }

        foreach ($this->relationsFor($parent) as $relation) {
            $this->deleteChildren($relation['model'], $relation['foreignKey'], $parent->getKey());
        }

        if ($parent instanceof Form) {
            $this->softDeleteFormNotifications($parent);
        }
    }

    public function restoreChildren(Model $parent): void
    {
        foreach ($this->relationsFor($parent) as $relation) {
            $this->restoreChildrenQuery($relation['model'], $relation['foreignKey'], $parent->getKey());
        }
    }

    public function forceDeleteChildren(Model $parent): void
    {
        foreach ($this->relationsFor($parent) as $relation) {
            $this->forceDeleteChildrenQuery($relation['model'], $relation['foreignKey'], $parent->getKey());
        }

        if ($parent instanceof Form) {
            $this->forceDeleteFormNotifications($parent);
        }
    }

    /**
     * @return list<array{model: class-string<Model>, foreignKey: string}>
     */
    private function relationsFor(Model $parent): array
    {
        return self::CHILD_RELATIONS[$parent::class] ?? [];
    }

    /**
     * @param  class-string<Model>  $modelClass
     */
    private function deleteChildren(string $modelClass, string $foreignKey, mixed $parentId): void
    {
        if (!$this->usesSoftDeletes($modelClass)) {
            $modelClass::query()->where($foreignKey, $parentId)->delete();

            return;
        }

        $modelClass::query()->where($foreignKey, $parentId)->delete();
    }

    /**
     * @param  class-string<Model>  $modelClass
     */
    private function restoreChildrenQuery(string $modelClass, string $foreignKey, mixed $parentId): void
    {
        if (!$this->usesSoftDeletes($modelClass)) {
            return;
        }

        $modelClass::onlyTrashed()
            ->where($foreignKey, $parentId)
            ->restore();
    }

    /**
     * @param  class-string<Model>  $modelClass
     */
    private function forceDeleteChildrenQuery(string $modelClass, string $foreignKey, mixed $parentId): void
    {
        $modelClass::withTrashed()
            ->where($foreignKey, $parentId)
            ->forceDelete();
    }

    /**
     * @param  class-string<Model>  $modelClass
     */
    private function usesSoftDeletes(string $modelClass): bool
    {
        return in_array(SoftDeletes::class, class_uses_recursive($modelClass), true);
    }

    private function softDeleteFormNotifications(Form $form): void
    {
        $notificationIds = $form->notifications()->pluck('id');

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

        FormNotificationTranslation::query()
            ->whereIn('form_notification_id', $notificationIds)
            ->delete();

        $form->notifications()->delete();
    }

    private function forceDeleteFormNotifications(Form $form): void
    {
        $notificationIds = $form->notifications()->pluck('id');

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

        FormNotificationTranslation::withTrashed()
            ->whereIn('form_notification_id', $notificationIds)
            ->forceDelete();

        $form->notifications()->delete();
    }
}
