<?php

namespace GriffinForms\Includes\Api;

use GriffinForms\Includes\Migration\MigrationRegistry;
use GriffinForms\Includes\Migration\MigrationRunner;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

class MigrationRest
{
    public const NAMESPACE = 'griffinforms/v1';

    private ?string $request_id = null;

    public function __construct()
    {
        add_action('rest_api_init', [$this, 'registerRoutes']);
    }

    public function registerRoutes(): void
    {
        register_rest_route(self::NAMESPACE, '/migration/start', [
            'methods' => WP_REST_Server::CREATABLE,
            'callback' => [$this, 'startMigration'],
            'permission_callback' => [$this, 'permissionManageMigration'],
        ]);

        register_rest_route(self::NAMESPACE, '/migration/progress', [
            'methods' => WP_REST_Server::READABLE,
            'callback' => [$this, 'getMigrationProgress'],
            'permission_callback' => [$this, 'permissionManageMigration'],
        ]);

        register_rest_route(self::NAMESPACE, '/migration/notice-dismiss', [
            'methods' => WP_REST_Server::CREATABLE,
            'callback' => [$this, 'dismissMigrationNotice'],
            'permission_callback' => [$this, 'permissionManageMigration'],
        ]);
    }

    public function permissionManageMigration(WP_REST_Request $request): bool|WP_Error
    {
        if (!current_user_can('manage_options')) {
            return new WP_Error(
                'griffinforms_forbidden',
                __('You do not have permission to manage migrations.', 'griffinforms-form-builder'),
                ['status' => 403]
            );
        }

        $nonce = (string) ($request->get_header('X-WP-Nonce') ?: $request->get_param('_wpnonce'));
        if ($nonce === '' || !wp_verify_nonce($nonce, 'wp_rest')) {
            return new WP_Error(
                'griffinforms_invalid_nonce',
                __('Invalid REST nonce.', 'griffinforms-form-builder'),
                ['status' => 403]
            );
        }

        return true;
    }

    public function startMigration(WP_REST_Request $request): WP_REST_Response
    {
        $source = sanitize_key((string) $request->get_param('source'));
        $source_ids = $request->get_param('source_ids');
        $source_ids = is_array($source_ids) ? $source_ids : [];
        $source_ids = array_values(array_filter(array_map('absint', $source_ids)));

        $registry = MigrationRegistry::getInstance();
        $adapter = $registry->getSource($source);
        if (!$adapter) {
            return $this->errorResponse(
                'griffinforms_migration_source_invalid',
                __('Migration source is not available.', 'griffinforms-form-builder'),
                400
            );
        }

        if (empty($source_ids)) {
            return $this->errorResponse(
                'griffinforms_migration_selection_empty',
                __('At least one source form must be selected.', 'griffinforms-form-builder'),
                400
            );
        }

        $runner = MigrationRunner::getInstance();
        $state = $runner->createJob($source, $source_ids, [
            'import_submissions' => (bool) $request->get_param('import_submissions'),
        ]);
        $state = $runner->advanceJob((string) ($state['job_id'] ?? ''));

        return $this->successResponse($state);
    }

    public function getMigrationProgress(WP_REST_Request $request): WP_REST_Response
    {
        $job_id = sanitize_key((string) $request->get_param('job_id'));
        if ($job_id === '') {
            return $this->errorResponse(
                'griffinforms_migration_job_missing',
                __('Migration job ID is required.', 'griffinforms-form-builder'),
                400
            );
        }

        $runner = MigrationRunner::getInstance();
        $state = $runner->getJobState($job_id);
        if (empty($state)) {
            return $this->errorResponse(
                'griffinforms_migration_job_not_found',
                __('Migration job was not found.', 'griffinforms-form-builder'),
                404
            );
        }

        if (($state['status'] ?? '') === 'running') {
            $state = $runner->advanceJob($job_id);
        }

        return $this->successResponse($state);
    }

    public function dismissMigrationNotice(WP_REST_Request $request): WP_REST_Response
    {
        $slug = sanitize_key((string) $request->get_param('slug'));
        $registry = MigrationRegistry::getInstance();
        $source = $registry->getSource($slug);

        if ($slug === '' || !$source) {
            return $this->errorResponse(
                'griffinforms_migration_notice_invalid',
                __('Migration notice source is invalid.', 'griffinforms-form-builder'),
                400
            );
        }

        \GriffinForms\Includes\Migration\MigrationNotice::getInstance()->dismiss($slug, get_current_user_id());

        return $this->successDataResponse([
            'slug' => $slug,
            'dismissed' => true,
        ]);
    }

    private function successResponse(array $state): WP_REST_Response
    {
        return $this->successDataResponse([
            'job_id' => sanitize_text_field((string) ($state['job_id'] ?? '')),
            'status' => sanitize_key((string) ($state['status'] ?? 'running')),
            'percent' => absint($state['percent'] ?? 0),
            'done' => !empty($state['done']),
            'processed' => absint($state['processed'] ?? 0),
            'total' => absint($state['total'] ?? 0),
            'result' => $state['result'] ?? [],
        ]);
    }

    private function successDataResponse(array $data): WP_REST_Response
    {
        return new WP_REST_Response([
            'success' => true,
            'data' => $data,
            'meta' => [
                'request_id' => $this->requestId(),
            ],
        ], 200);
    }

    private function errorResponse(string $code, string $message, int $status): WP_REST_Response
    {
        return new WP_REST_Response([
            'success' => false,
            'data' => [
                'code' => sanitize_key($code),
                'message' => sanitize_text_field($message),
            ],
            'meta' => [
                'request_id' => $this->requestId(),
            ],
        ], $status);
    }

    private function requestId(): string
    {
        if ($this->request_id === null) {
            $this->request_id = substr(hash('sha256', (string) microtime(true) . '|' . wp_rand()), 0, 16);
        }

        return $this->request_id;
    }
}
