<?php

namespace GriffinForms\Includes\Migration;

use GriffinForms\Traits\Singleton;

class MigrationNotice
{
    use Singleton;

    /** @var array<string, array<int, array<string, mixed>>> Per-request getForms() cache keyed by source slug. */
    private array $forms_cache = [];

    /**
     * @return MigrationSourceInterface[]
     */
    public function getDetectedSources(): array
    {
        $registry = MigrationRegistry::getInstance();
        $detected = [];

        foreach ($registry->getInstalledSources() as $source) {
            $forms = $this->getSourceForms($source);
            if (!empty($forms)) {
                $detected[] = $source;
            }
        }

        return $detected;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function getRemainingForms(MigrationSourceInterface $source): array
    {
        $all = $this->getSourceForms($source);
        if (empty($all)) {
            return [];
        }

        $imported = $this->getImportedSourceIds($source->getSlug());
        if (empty($imported)) {
            return $all;
        }

        return array_values(array_filter($all, static function (array $form) use ($imported): bool {
            return !in_array(absint($form['source_id'] ?? 0), $imported, true);
        }));
    }

    /**
     * Returns getForms() result from cache — one DB hit per source per request.
     *
     * @return array<int, array<string, mixed>>
     */
    private function getSourceForms(MigrationSourceInterface $source): array
    {
        $slug = $source->getSlug();
        if (!array_key_exists($slug, $this->forms_cache)) {
            try {
                $this->forms_cache[$slug] = (array) $source->getForms();
            } catch (\Throwable $throwable) {
                $this->forms_cache[$slug] = [];
            }
        }

        return $this->forms_cache[$slug];
    }

    public function recordImported(string $source_slug, array $source_ids): void
    {
        $source_slug = sanitize_key($source_slug);
        $existing = $this->getImportedSourceIds($source_slug);
        $merged = array_values(array_unique(array_merge($existing, array_map('absint', $source_ids))));
        update_option($this->importedOptionKey($source_slug), $merged, false);
    }

    /**
     * @return int[]
     */
    private function getImportedSourceIds(string $source_slug): array
    {
        $stored = get_option($this->importedOptionKey(sanitize_key($source_slug)), []);
        return is_array($stored) ? array_map('absint', $stored) : [];
    }

    private function importedOptionKey(string $source_slug): string
    {
        return 'griffinforms_migrated_source_ids_' . sanitize_key($source_slug);
    }

    public function isDismissed(string $slug, int $user_id): bool
    {
        $user_id = absint($user_id);
        if ($user_id <= 0) {
            return false;
        }

        return (bool) get_user_meta($user_id, $this->getMetaKey($slug), true);
    }

    public function dismiss(string $slug, int $user_id): void
    {
        $user_id = absint($user_id);
        if ($user_id <= 0) {
            return;
        }

        update_user_meta($user_id, $this->getMetaKey($slug), 1);
    }

    private function getMetaKey(string $slug): string
    {
        return 'griffinforms_migration_notice_dismissed_' . sanitize_key($slug);
    }
}
