<?php

if (! defined('ABSPATH')) {
    exit;
} class PufferGo_Workflow_Jobs_REST
{
    public const UPSTREAM_CREATE_TIMEOUT_SECONDS = 20;

    public const UPSTREAM_CREATE_BULK_TIMEOUT_SECONDS = 45;

    public const WORKFLOW_JOBS_BULK_MAX = 8;

    public const UPSTREAM_GET_TIMEOUT_SECONDS = 15;

    public const JOB_TYPE_STRUCTURE = 'structure-data';

    public const JOB_TYPE_SITE_ARCHITECTURE = 'site-architecture';

    public const JOB_TYPE_PAGE_BLOCK_PLANNING = 'page-block-planning';

    public const JOB_TYPE_TAILWIND_GENERATE = 'tailwind-generate';

    public const TEMPLATE_MANIFEST_RELATIVE_PATH = 'templates/patterns/manifest.generated.json';

    private static function default_language(): string
    {
        $profile = get_option(PufferGo_Site_Profile::OPTION_KEY, array());

        return (is_array($profile) && ! empty($profile['language'])) ? (string) $profile['language'] : 'en';
    }

    private function allowed_job_types()
    {
        return array(self::JOB_TYPE_STRUCTURE, self::JOB_TYPE_SITE_ARCHITECTURE, self::JOB_TYPE_PAGE_BLOCK_PLANNING, self::JOB_TYPE_TAILWIND_GENERATE, );
    }

    private static function default_client_poll_config()
    {
        return array('maxWaitMs' => 6 * 60 * 1000, 'createJobPath' => 'puffergo/v1/workflow/jobs', 'getJobPathPrefix' => 'puffergo/v1/workflow/jobs', 'pollJobStatusPath' => 'puffergo/v1/workflow/jobs/status', );
    }

    public static function add_poll_config_inline_script($handle)
    {
        if (! is_string($handle) || '' === $handle) {
            return;
        } $config = apply_filters('puffergo_workflow_job_poll_config', self::default_client_poll_config());

        if (! is_array($config)) {
            $config = array();
        } $json = wp_json_encode($config, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES);

        if (false === $json) {
            return;
        } wp_add_inline_script($handle, 'var puffergoWorkflowJobPoll = ' . $json . ';', 'before');
    }

    public function __construct()
    {
        add_action('rest_api_init', array($this, 'register_routes'));
    }

    public function register_routes()
    {
        register_rest_route('puffergo/v1', '/workflow/jobs/status', array('methods' => 'POST', 'callback' => array($this, 'post_jobs_status'), 'permission_callback' => array($this, 'check_permission'), ));
        register_rest_route('puffergo/v1', '/workflow/jobs', array('methods' => 'POST', 'callback' => array($this, 'create_job'), 'permission_callback' => array($this, 'check_permission'), ));
        register_rest_route('puffergo/v1', '/workflow/jobs/(?P<job_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', array('methods' => 'GET', 'callback' => array($this, 'get_job'), 'permission_callback' => array($this, 'check_permission'), ));
        register_rest_route('puffergo/v1', '/templates', array('methods' => 'GET', 'callback' => array($this, 'list_templates'), 'permission_callback' => array($this, 'check_permission'), 'args' => array('blockType' => array('type' => 'string', 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ), ), ));
    }

    public function list_templates($request)
    {
        $want = strtoupper(str_replace(array(' ', '-'), '_', (string) $request->get_param('blockType')));
        $manifest = $this->read_json_file(PUFFERGO_PLUGIN_DIR . '/' . self::TEMPLATE_MANIFEST_RELATIVE_PATH);

        if (! is_array($manifest)) {
            return new WP_REST_Response(array('success' => true, 'items' => array(), ), 200);
        } $items = array();

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

if (! isset($item['ai']['eligible']) || true !== $item['ai']['eligible']) {
                continue;
            } $bt = isset($item['blockType']) ? strtoupper(str_replace(array(' ', '-'), '_', (string) $item['blockType'])) : '';

            if ('' !== $want && $bt !== $want) {
                continue;
            } $id = isset($item['templateId']) ? (string) $item['templateId'] : '';

            if ('' === $id) {
                continue;
            } $preview_url = '';
            $source_path = isset($item['sourcePath']) ? (string) $item['sourcePath'] : '';

            if ('' !== $source_path) {
                $preview_url = rtrim(PUFFERGO_AI_PAGE_BUILDER_BASE_URL, '/') . '/assets/patterns/' . trim($source_path, '/') . '/preview.webp';
                $preview_version = isset($item['previewVersion']) ? (string) $item['previewVersion'] : '';

                if ('' !== $preview_version) {
                    $preview_url .= '?v=' . rawurlencode($preview_version);
                }
            } $vc = '';

            if (isset($item['visualComposition']['layout'])) {
                $vc = (string) $item['visualComposition']['layout'];
            } $items[] = array('id' => $id, 'blockType' => $bt, 'title' => isset($item['title']) ? (string) $item['title'] : $id, 'description' => isset($item['description']) ? (string) $item['description'] : '', 'previewUrl' => $preview_url, 'vc' => $vc, 'zh' => isset($item['localeSummary']['zh']) ? (string) $item['localeSummary']['zh'] : '', );
        }

return new WP_REST_Response(array('success' => true, 'blockType' => '' !== $want ? $want : null, 'items' => $items, ), 200);
    }

    public function check_permission()
    {
        return current_user_can('edit_posts');
    }

    public function create_job($request)
    {
        $params = $request->get_json_params();

        if (! is_array($params)) {
            $params = array();
        }

if (isset($params['jobs']) && is_array($params['jobs'])) {
            return $this->create_jobs_bulk($params['jobs']);
        } $type = isset($params['type']) ? sanitize_key($params['type']) : '';

        if (! in_array($type, $this->allowed_job_types(), true)) {
            return new WP_REST_Response(array('success' => false, 'message' => __('Invalid job type.', 'puffergo'), ), 400);
        } $payload = isset($params['payload']) && is_array($params['payload']) ? $params['payload'] : array();

        try {
            $body = $this->build_upstream_body($type, $payload);
        } catch (Exception $e) {
            return new WP_REST_Response(array('success' => false, 'message' => $e->getMessage(), ), 400);
        }

try {
            $api = PufferGo_API_Client::get_instance();
            $result = $api->post('/api/workflow/jobs', array('type' => $type, 'payload' => $body, ), array('timeout' => self::UPSTREAM_CREATE_TIMEOUT_SECONDS, 'log_prefix' => 'Workflow Job Create', 'success_http_codes' => array(200, 202), 'return_raw_result' => true, ));
            $http_status = isset($result['jobId']) ? 202 : 200;

            return new WP_REST_Response($result, $http_status);
        } catch (PufferGo_API_Exception $e) {
            return new WP_REST_Response($e->toArray(), $e->getCode());
        } catch (Exception $e) {
            return new WP_REST_Response(array('success' => false, 'message' => $e->getMessage(), ), 500);
        }
    }

    private function create_jobs_bulk(array $jobs_in)
    {
        $n = count($jobs_in);

        if ($n < 1 || $n > self::WORKFLOW_JOBS_BULK_MAX) {
            return new WP_REST_Response(array('success' => false, 'message' => sprintf(__('Bulk jobs count must be between 1 and %d.', 'puffergo'), self::WORKFLOW_JOBS_BULK_MAX), ), 400);
        } $upstream_jobs = array();

        foreach ($jobs_in as $item) {
            if (! is_array($item)) {
                return new WP_REST_Response(array('success' => false, 'message' => __('Invalid job entry.', 'puffergo'), ), 400);
            } $type = isset($item['type']) ? sanitize_key($item['type']) : '';

            if (! in_array($type, $this->allowed_job_types(), true)) {
                return new WP_REST_Response(array('success' => false, 'message' => __('Invalid job type.', 'puffergo'), ), 400);
            } $payload = isset($item['payload']) && is_array($item['payload']) ? $item['payload'] : array();

            try {
                $upstream_jobs[] = array('type' => $type, 'payload' => $this->build_upstream_body($type, $payload), );
            } catch (Exception $e) {
                return new WP_REST_Response(array('success' => false, 'message' => $e->getMessage(), ), 400);
            }
        }

try {
            $api = PufferGo_API_Client::get_instance();
            $result = $api->post('/api/workflow/jobs', array('jobs' => $upstream_jobs), array('timeout' => self::UPSTREAM_CREATE_BULK_TIMEOUT_SECONDS, 'log_prefix' => 'Workflow Job Create Bulk', 'success_http_codes' => array(200, 202), 'return_raw_result' => true, ));

            return new WP_REST_Response($result, 202);
        } catch (PufferGo_API_Exception $e) {
            return new WP_REST_Response($e->toArray(), $e->getCode());
        } catch (Exception $e) {
            return new WP_REST_Response(array('success' => false, 'message' => $e->getMessage(), ), 500);
        }
    }

    public function post_jobs_status($request)
    {
        $params = $request->get_json_params();

        if (! is_array($params)) {
            $params = array();
        } $ids = isset($params['ids']) && is_array($params['ids']) ? $params['ids'] : array();
        $n = count($ids);

        if ($n < 1 || $n > self::WORKFLOW_JOBS_BULK_MAX) {
            return new WP_REST_Response(array('success' => false, 'message' => sprintf(__('ids must contain between 1 and %d UUIDs.', 'puffergo'), self::WORKFLOW_JOBS_BULK_MAX), ), 400);
        } $uuid_pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i';
        $sanitized = array();

        foreach ($ids as $id) {
            $s = sanitize_text_field((string) $id);

            if (! preg_match($uuid_pattern, $s)) {
                return new WP_REST_Response(array('success' => false, 'message' => __('Invalid job id format.', 'puffergo'), ), 400);
            } $sanitized[] = strtolower($s);
        }

try {
            $api = PufferGo_API_Client::get_instance();
            $result = $api->post('/api/workflow/jobs/status', array('ids' => $sanitized), array('timeout' => self::UPSTREAM_GET_TIMEOUT_SECONDS, 'log_prefix' => 'Workflow Job Status Bulk', 'success_http_codes' => array(200), 'return_raw_result' => true, ));

            return new WP_REST_Response($result, 200);
        } catch (PufferGo_API_Exception $e) {
            return new WP_REST_Response($e->toArray(), $e->getCode());
        } catch (Exception $e) {
            return new WP_REST_Response(array('success' => false, 'message' => $e->getMessage(), ), 500);
        }
    }

    public function get_job($request)
    {
        $job_id = isset($request['job_id']) ? sanitize_text_field($request['job_id']) : '';

        if ('' === $job_id) {
            return new WP_REST_Response(array('success' => false, 'message' => __('Missing job id.', 'puffergo'), ), 400);
        }

try {
            $api = PufferGo_API_Client::get_instance();
            $result = $api->get('/api/workflow/jobs/' . rawurlencode($job_id), array(), array('timeout' => self::UPSTREAM_GET_TIMEOUT_SECONDS, 'log_prefix' => 'Workflow Job Poll', 'return_raw_result' => true, 'return_body_on_client_error' => true, ));

            if (isset($result['_httpStatus'])) {
                $code = (int) $result['_httpStatus'];
                unset($result['_httpStatus']);

                return new WP_REST_Response($result, $code);
            }

return new WP_REST_Response($result, 200);
        } catch (PufferGo_API_Exception $e) {
            return new WP_REST_Response($e->toArray(), $e->getCode());
        } catch (Exception $e) {
            return new WP_REST_Response(array('success' => false, 'message' => $e->getMessage(), ), 500);
        }
    }

    private function build_upstream_body($type, array $payload)
    {
        switch ($type) {
            case self::JOB_TYPE_STRUCTURE: return $this->build_structure_payload($payload);
            case self::JOB_TYPE_SITE_ARCHITECTURE: return $this->build_site_architecture_payload($payload);
            case self::JOB_TYPE_PAGE_BLOCK_PLANNING: return $this->build_page_block_planning_payload($payload);
            case self::JOB_TYPE_TAILWIND_GENERATE: return $this->build_tailwind_generate_payload($payload);
            default: throw new Exception(__('Unsupported job type.', 'puffergo'));
        }
    }

    private function build_tailwind_generate_payload(array $payload)
    {
        if (! class_exists('PufferGo_Tailwind_Container')) {
            throw new Exception(__('Tailwind module is not available.', 'puffergo'));
        }

return PufferGo_Tailwind_Container::instance()->build_generate_block_request_body($payload);
    }

    private function build_structure_payload(array $payload)
    {
        $raw = isset($payload['rawInput']) ? sanitize_textarea_field(wp_unslash((string) $payload['rawInput'])) : '';

        if ('' === trim($raw)) {
            throw new Exception(__('Please provide user input information', 'puffergo'));
        }

if (mb_strlen(trim($raw)) < 10) {
            throw new Exception(__('Input content must be at least 10 characters', 'puffergo'));
        } $language = isset($payload['language']) ? sanitize_text_field(wp_unslash((string) $payload['language'])) : self::default_language();
        $model = isset($payload['model']) ? sanitize_text_field(wp_unslash((string) $payload['model'])) : 'gemini-3-pro-preview';
        $out = array('rawInput' => $raw, 'language' => $language, 'model' => $model, );

        if (isset($payload['aiScene']) && is_string($payload['aiScene'])) {
            $out['aiScene'] = sanitize_text_field(wp_unslash($payload['aiScene']));
        }

if (isset($payload['keywords']) && is_array($payload['keywords'])) {
            $kw = array_values(array_filter(array_map(static function ($k) { return sanitize_text_field((string) $k); }, $payload['keywords'])));

            if (! empty($kw)) {
                $out['keywords'] = array_slice($kw, 0, 30);
            }
        }

return $out;
    }

    private function build_site_architecture_payload(array $payload)
    {
        $structured = isset($payload['structuredData']) ? $payload['structuredData'] : null;

        if (empty($structured) || ! is_array($structured)) {
            throw new Exception(__('Please provide structured data', 'puffergo'));
        } $language = isset($payload['language']) ? sanitize_text_field(wp_unslash((string) $payload['language'])) : self::default_language();
        $model = isset($payload['model']) ? sanitize_text_field(wp_unslash((string) $payload['model'])) : 'gemini-3-pro-preview';
        $body = array('structuredData' => $structured, 'language' => $language, 'model' => $model, 'systemPages' => $this->build_system_pages($language), );

        if (isset($payload['siteDesignContract']) && is_array($payload['siteDesignContract'])) {
            $body['siteDesignContract'] = $payload['siteDesignContract'];
        }

if (isset($payload['aiScene']) && is_string($payload['aiScene'])) {
            $body['aiScene'] = sanitize_text_field(wp_unslash($payload['aiScene']));
        }

return $body;
    }

    private function build_system_pages(string $language): array
    {
        $is_zh = 'zh_CN' === $language;
        $pages = array();
        $enabled = get_option('puffergo_enabled_modules', PufferGo_Base_CPT::DEFAULT_MODULES);

        if (! is_array($enabled)) {
            $enabled = PufferGo_Base_CPT::DEFAULT_MODULES;
        } $product_slug = sanitize_title((string) get_option('puffergo_product_slug', 'products'));
        $pages[] = array('name' => $is_zh ? '产品' : 'Products', 'path' => '/' . $product_slug, 'pageType' => 'PRODUCT_LIST_PAGE', 'action' => 'link_only', 'locked' => true, );
        $pages[] = array('name' => $is_zh ? '博客' : 'Blog', 'path' => '/blog', 'pageType' => 'BLOG_LIST_PAGE', 'action' => 'link_only', 'locked' => true, );

        if (in_array('solution', $enabled, true)) {
            $slug = sanitize_title((string) get_option('puffergo_solution_slug', 'solutions'));
            $pages[] = array('name' => $is_zh ? '解决方案' : 'Solutions', 'path' => '/' . $slug, 'pageType' => 'SOLUTIONS_PAGE', 'action' => 'link_only', 'locked' => true, );
        }

if (in_array('case-study', $enabled, true)) {
            $slug = sanitize_title((string) get_option('puffergo_case_slug', 'case-studies'));
            $pages[] = array('name' => $is_zh ? '案例研究' : 'Case Studies', 'path' => '/' . $slug, 'pageType' => 'CASE_STUDY_LIST_PAGE', 'action' => 'link_only', 'locked' => true, );
        }

if (in_array('testimonial', $enabled, true)) {
            $slug = sanitize_title((string) get_option('puffergo_testimonial_slug', 'testimonials'));
            $pages[] = array('name' => $is_zh ? '客户见证' : 'Testimonials', 'path' => '/' . $slug, 'pageType' => 'TESTIMONIALS_PAGE', 'action' => 'link_only', 'locked' => true, );
        }

return $pages;
    }

    private function build_page_block_planning_payload(array $payload)
    {
        $pages = isset($payload['pages']) && is_array($payload['pages']) ? $payload['pages'] : array();

        if (empty($pages)) {
            throw new Exception(__('Please provide valid page information', 'puffergo'));
        }

foreach ($pages as $index => $page) {
            if (! is_array($page)) {
                throw new Exception(__('Please provide valid page information', 'puffergo'));
            }

if (! isset($pages[ $index ]['name'])) {
                $pages[ $index ]['name'] = '';
            }

if (! isset($pages[ $index ]['path'])) {
                $pages[ $index ]['path'] = '';
            }

if (empty($page['purpose'])) {
                throw new Exception(__('Page description cannot be empty', 'puffergo'));
            }
        } $language = isset($payload['language']) ? sanitize_text_field(wp_unslash((string) $payload['language'])) : self::default_language();
        $model = isset($payload['model']) ? sanitize_text_field(wp_unslash((string) $payload['model'])) : 'gemini-3-pro-preview';
        $saved_profile = get_option('puffergo_site_profile', array());

        if (! is_array($saved_profile)) {
            $saved_profile = array();
        }

if (isset($saved_profile['updated_at'])) {
            unset($saved_profile['updated_at']);
        } $body = array('structuredData' => $saved_profile, 'pages' => $pages, 'language' => $language, 'model' => $model, );

        if (isset($payload['designSystem'])) {
            $body['designSystem'] = $payload['designSystem'];
        }

if (isset($payload['globalElements'])) {
            $body['globalElements'] = $payload['globalElements'];
        }

if (isset($payload['siteDesignContract']) && is_array($payload['siteDesignContract'])) {
            $body['siteDesignContract'] = $payload['siteDesignContract'];
        } $catalog = $this->build_template_catalog($payload);

        if (! empty($catalog)) {
            $body['templateSelectionContext'] = array('catalog' => $catalog);
        }

if (isset($payload['aiScene']) && is_string($payload['aiScene'])) {
            $body['aiScene'] = sanitize_text_field(wp_unslash($payload['aiScene']));
        }

return $body;
    }

    private function build_template_catalog(array $payload)
    {
        if (isset($payload['templateSelectionContext']['catalog']) && is_array($payload['templateSelectionContext']['catalog'])) {
            return $payload['templateSelectionContext']['catalog'];
        } $manifest = $this->read_json_file(PUFFERGO_PLUGIN_DIR . '/' . self::TEMPLATE_MANIFEST_RELATIVE_PATH);

        if (! is_array($manifest)) {
            return array();
        } $current_industry = isset($payload['templateSelectionContext']['industry']) ? sanitize_key((string) $payload['templateSelectionContext']['industry']) : '';
        $pages = isset($payload['pages']) && is_array($payload['pages']) ? $payload['pages'] : array();
        $single_page_type = '';

        if (count($pages) === 1 && ! empty($pages[0]['pageType'])) {
            $single_page_type = sanitize_key((string) $pages[0]['pageType']);
        } $catalog = array();

        foreach ($manifest as $item) {
            if (! is_array($item)) {
                continue;
            } $raw_id = isset($item['templateId']) ? (string) $item['templateId'] : (isset($item['id']) ? (string) $item['id'] : '');
            $id = sanitize_key($raw_id);

            if ('' === $id) {
                continue;
            }

if (! isset($item['ai']['eligible']) || true !== $item['ai']['eligible']) {
                continue;
            }

if ('' !== $current_industry) {
                $industries = isset($item['industries']) && is_array($item['industries']) ? $item['industries'] : array('*');

                if (! in_array('*', $industries, true) && ! in_array($current_industry, $industries, true)) {
                    continue;
                }
            }

if ('' !== $single_page_type) {
                $never = isset($item['pageScope']['never']) && is_array($item['pageScope']['never']) ? $item['pageScope']['never'] : array();

                if (in_array($single_page_type, $never, true)) {
                    continue;
                }
            } $block_type = isset($item['blockType']) ? sanitize_key((string) $item['blockType']) : 'other';

            if ('' === $block_type) {
                continue;
            } $entry = array('id' => $id);
            $vc_obj = isset($item['visualComposition']) && is_array($item['visualComposition']) ? $item['visualComposition'] : null;
            $vc = isset($vc_obj['layout']) ? (string) $vc_obj['layout'] : '';

            if ('' !== $vc) {
                $entry['vc'] = $vc;
            } $zh = isset($item['localeSummary']['zh']) ? (string) $item['localeSummary']['zh'] : '';

            if ('' !== $zh) {
                $entry['zh'] = $zh;
            }

if (isset($item['pageScope']) && is_array($item['pageScope'])) {
                $scope = array();

                if (isset($item['pageScope']['only']) && is_array($item['pageScope']['only'])) {
                    $only = array_values(array_filter($item['pageScope']['only'], 'is_string'));

                    if (count($only) > 0) {
                        $scope['only'] = $only;
                    }
                }

if (isset($item['pageScope']['never']) && is_array($item['pageScope']['never'])) {
                    $never = array_values(array_filter($item['pageScope']['never'], 'is_string'));

                    if (count($never) > 0) {
                        $scope['never'] = $never;
                    }
                }

if (count($scope) > 0) {
                    $entry['pageScope'] = $scope;
                }
            }

if (! isset($catalog[$block_type])) {
                $catalog[$block_type] = array();
            } $catalog[$block_type][] = $entry;
        }

return $catalog;
    }

    private function read_json_file($path)
    {
        if (! is_string($path) || '' === $path || ! is_readable($path)) {
            return null;
        } $json = file_get_contents($path);

        if (false === $json || '' === $json) {
            return null;
        } $data = json_decode($json, true);

        return is_array($data) ? $data : null;
    }
}
