<?php

namespace WPSN\Modules\Onboarding;

defined('ABSPATH') || exit;

/**
 * Stores manual visibility/acknowledgement state for setup checklist tasks.
 *
 * Important: this does NOT change the real checklist completion status.
 * It only lets users hide or acknowledge recurring setup reminders until the
 * underlying configuration changes and the task disappears naturally.
 */
final class OnboardingChecklistStateRepository
{
    private const OPTION_NAME = 'wpsn_onboarding_checklist_task_state';

    /**
     * @return array<string,array{state:string,updated_at:string}>
     */
    public function all(): array
    {
        $raw = get_option(self::OPTION_NAME, []);
        if (!is_array($raw)) {
            return [];
        }

        $out = [];
        foreach ($raw as $key => $row) {
            $key = sanitize_key((string) $key);
            if ($key === '') {
                continue;
            }

            $normalized = $this->normalizeRow($row);
            if ($normalized === null) {
                continue;
            }

            $out[$key] = $normalized;
        }

        return $out;
    }

    /**
     * @param array<int,array<string,mixed>> $items
     * @return array<int,array<string,mixed>>
     */
    public function apply(array $items): array
    {
        $states = $this->all();
        $todoKeys = [];
        $changed = false;

        foreach ($items as $idx => $item) {
            if (!is_array($item)) {
                continue;
            }

            $key = isset($item['key']) ? sanitize_key((string) $item['key']) : '';
            $status = isset($item['status']) ? sanitize_key((string) $item['status']) : '';

            if ($key !== '' && $status === 'todo') {
                $todoKeys[$key] = true;
                $row = $states[$key] ?? null;
                $taskState = is_array($row) ? (string) ($row['state'] ?? 'open') : 'open';
                if (!in_array($taskState, ['open', 'done', 'hidden'], true)) {
                    $taskState = 'open';
                }

                $item['task_state'] = $taskState;
                if (is_array($row) && !empty($row['updated_at'])) {
                    $item['task_state_updated_at'] = (string) $row['updated_at'];
                }
            } else {
                $item['task_state'] = $status === 'done' ? 'resolved' : 'open';
            }

            $items[$idx] = $item;
        }

        foreach ($states as $key => $row) {
            if (!isset($todoKeys[$key])) {
                unset($states[$key]);
                $changed = true;
            }
        }

        if ($changed) {
            $this->persist($states);
        }

        return $items;
    }

    public function stateFor(string $key): string
    {
        $key = sanitize_key($key);
        if ($key === '') {
            return 'open';
        }

        $row = $this->all()[$key] ?? null;
        if (!is_array($row)) {
            return 'open';
        }

        $state = sanitize_key((string) ($row['state'] ?? 'open'));
        return in_array($state, ['done', 'hidden'], true) ? $state : 'open';
    }

    /**
     * @param array<int,array<string,mixed>> $items
     */
    public function setState(string $key, string $state, array $items = []): bool
    {
        $key = sanitize_key($key);
        $state = sanitize_key($state);

        if ($key === '' || !in_array($state, ['done', 'hidden'], true)) {
            return false;
        }

        if (!$this->isValidTodoKey($key, $items)) {
            return false;
        }

        $all = $this->all();
        $all[$key] = [
            'state' => $state,
            'updated_at' => gmdate('Y-m-d H:i:s'),
        ];

        return $this->persist($all);
    }

    public function clearState(string $key): bool
    {
        $key = sanitize_key($key);
        if ($key === '') {
            return false;
        }

        $all = $this->all();
        if (!isset($all[$key])) {
            return true;
        }

        unset($all[$key]);
        return $this->persist($all);
    }

    /**
     * @param array<int,string> $keys
     * @param array<int,array<string,mixed>> $items
     */
    public function bulkSetState(array $keys, string $state, array $items = []): int
    {
        $state = sanitize_key($state);
        if (!in_array($state, ['done', 'hidden'], true)) {
            return 0;
        }

        $keys = $this->normalizeKeys($keys);
        if (empty($keys)) {
            return 0;
        }

        $all = $this->all();
        $count = 0;
        $updatedAt = gmdate('Y-m-d H:i:s');

        foreach ($keys as $key) {
            if (!$this->isValidTodoKey($key, $items)) {
                continue;
            }

            $all[$key] = [
                'state' => $state,
                'updated_at' => $updatedAt,
            ];
            $count++;
        }

        if ($count <= 0) {
            return 0;
        }

        $this->persist($all);
        return $count;
    }

    /**
     * @param array<int,string> $keys
     */
    public function bulkClearState(array $keys): int
    {
        $keys = $this->normalizeKeys($keys);
        if (empty($keys)) {
            return 0;
        }

        $all = $this->all();
        $count = 0;

        foreach ($keys as $key) {
            if (!isset($all[$key])) {
                continue;
            }

            unset($all[$key]);
            $count++;
        }

        if ($count <= 0) {
            return 0;
        }

        $this->persist($all);
        return $count;
    }

    /**
     * @param mixed $row
     * @return array{state:string,updated_at:string}|null
     */
    private function normalizeRow($row): ?array
    {
        if (!is_array($row)) {
            return null;
        }

        $state = isset($row['state']) ? sanitize_key((string) $row['state']) : '';
        if (!in_array($state, ['done', 'hidden'], true)) {
            return null;
        }

        $updatedAt = isset($row['updated_at']) ? trim((string) $row['updated_at']) : '';
        if ($updatedAt === '') {
            $updatedAt = gmdate('Y-m-d H:i:s');
        }

        return [
            'state' => $state,
            'updated_at' => $updatedAt,
        ];
    }

    /**
     * @param array<int,string> $keys
     * @return array<int,string>
     */
    private function normalizeKeys(array $keys): array
    {
        $out = [];
        foreach ($keys as $key) {
            $key = sanitize_key((string) $key);
            if ($key === '' || in_array($key, $out, true)) {
                continue;
            }
            $out[] = $key;
        }

        return $out;
    }

    /**
     * @param array<int,array<string,mixed>> $items
     */
    private function isValidTodoKey(string $key, array $items): bool
    {
        if (empty($items)) {
            return true;
        }

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }

            $itemKey = isset($item['key']) ? sanitize_key((string) $item['key']) : '';
            $status = isset($item['status']) ? sanitize_key((string) $item['status']) : '';

            if ($itemKey === $key && $status === 'todo') {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array<string,array{state:string,updated_at:string}> $rows
     */
    private function persist(array $rows): bool
    {
        return update_option(self::OPTION_NAME, $rows, false);
    }
}
