<?php

namespace Pixeldev\SquareWooSync\REST;

use Pixeldev\SquareWooSync\Abstracts\RESTController;
use Pixeldev\SquareWooSync\Admin\Helpers\LegacyAdminHelpers;
use Pixeldev\SquareWooSync\Logger\DebugLogger;
use Pixeldev\SquareWooSync\Security\SquareTokenManager;
use Pixeldev\SquareWooSync\Tools\ApiTester;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * REST endpoints powering the React Tools page in the free plugin.
 *
 * Most actions delegate to small helpers in LegacyAdminHelpers / ApiTester /
 * DebugLogger so the logic isn't duplicated. The Pro plugin adds further
 * tools (saved-card removal, advanced metadata helpers) on top of this set.
 */
class ToolsController extends RESTController
{
    protected $namespace = 'sws/v1';
    protected $base      = 'tools';

    private const TOKEN_PROXY_REFRESH_URL = 'https://proxy.squaresyncforwoo.com/wp-json/square-oauth/v1/refresh-token';

    public function register_routes()
    {
        $ns = $this->namespace;
        $b  = $this->base;

        // Cleanup actions
        register_rest_route($ns, "/{$b}/cleanup-customer-meta", [[
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'cleanup_customer_meta'],
            'permission_callback' => [$this, 'check_permission'],
        ]]);
        register_rest_route($ns, "/{$b}/cleanup-product-meta", [[
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'cleanup_product_meta'],
            'permission_callback' => [$this, 'check_permission'],
        ]]);
        register_rest_route($ns, "/{$b}/regenerate-slugs", [[
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'regenerate_slugs'],
            'permission_callback' => [$this, 'check_permission'],
        ]]);

        // Diagnostics
        register_rest_route($ns, "/{$b}/scan-duplicates", [[
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => [$this, 'scan_duplicates'],
            'permission_callback' => [$this, 'check_permission'],
        ]]);
        register_rest_route($ns, "/{$b}/api-test", [[
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'api_test'],
            'permission_callback' => [$this, 'check_permission'],
        ]]);
        register_rest_route($ns, "/{$b}/token-refresh-test", [[
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'token_refresh_test'],
            'permission_callback' => [$this, 'check_permission'],
        ]]);

        register_rest_route($ns, "/{$b}/cron-test", [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'cron_test_status'],
                'permission_callback' => [$this, 'check_permission'],
            ],
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [$this, 'cron_test_run'],
                'permission_callback' => [$this, 'check_permission'],
            ],
        ]);

        // Debug log
        register_rest_route($ns, "/{$b}/log", [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'log_read'],
                'permission_callback' => [$this, 'check_permission'],
            ],
            [
                'methods'             => WP_REST_Server::DELETABLE,
                'callback'            => [$this, 'log_clear'],
                'permission_callback' => [$this, 'check_permission'],
            ],
        ]);
    }

    /* ------------------------------------------------------------------ *
     * Cleanup
     * ------------------------------------------------------------------ */

    public function cleanup_customer_meta(): WP_REST_Response
    {
        (new LegacyAdminHelpers())->remove_customer_meta();
        return new WP_REST_Response(['success' => true, 'message' => 'All customer metadata removed.']);
    }

    public function cleanup_product_meta(): WP_REST_Response
    {
        (new LegacyAdminHelpers())->remove_product_meta();
        return new WP_REST_Response(['success' => true, 'message' => 'All product metadata removed.']);
    }

    public function regenerate_slugs(): WP_REST_Response
    {
        $updated = (new LegacyAdminHelpers())->regenerate_product_slugs();
        return new WP_REST_Response([
            'success' => true,
            'updated' => (int) $updated,
            'message' => sprintf('Updated %d product slugs.', (int) $updated),
        ]);
    }

    /* ------------------------------------------------------------------ *
     * Diagnostics
     * ------------------------------------------------------------------ */

    public function scan_duplicates(): WP_REST_Response
    {
        $groups = (new LegacyAdminHelpers())->scan_duplicate_square_links();
        foreach ($groups as &$g) {
            foreach ($g['products'] as &$p) {
                $p['edit_link'] = get_edit_post_link(
                    'product_variation' === $p['type'] ? (int) ($p['parent'] ?: $p['id']) : (int) $p['id'],
                    'raw'
                );
            }
        }
        unset($g, $p);
        return new WP_REST_Response(['groups' => $groups]);
    }

    public function api_test(WP_REST_Request $request): WP_REST_Response
    {
        $endpoint = (string) $request->get_param('endpoint');
        $method   = strtoupper((string) ($request->get_param('method') ?: 'GET'));
        $body     = $request->get_param('body');
        $body     = is_string($body) ? $body : null;

        if ('' === $endpoint) {
            return new WP_REST_Response(['success' => false, 'message' => 'Endpoint required.'], 400);
        }

        $tester = new ApiTester();
        $result = $tester->run_request($endpoint, $method, $body);
        return new WP_REST_Response($result);
    }

    public function token_refresh_test(): WP_REST_Response
    {
        $refresh = SquareTokenManager::get_refresh();
        if (!$refresh) {
            return new WP_REST_Response(['success' => false, 'error' => 'No refresh token found']);
        }

        $settings = get_option('square-woo-sync_settings', []);
        $env      = (($settings['environment'] ?? 'live') === 'sandbox') ? 'sandbox' : 'live';

        $response = wp_remote_post(
            self::TOKEN_PROXY_REFRESH_URL,
            [
                'headers' => ['Content-Type' => 'application/json'],
                'body'    => wp_json_encode(['env' => $env, 'refresh_token' => $refresh]),
                'timeout' => 20,
            ]
        );

        if (is_wp_error($response)) {
            return new WP_REST_Response(['success' => false, 'error' => $response->get_error_message()]);
        }

        $code = wp_remote_retrieve_response_code($response);
        $data = json_decode(wp_remote_retrieve_body($response), true);

        if (200 === $code && isset($data['access_token'], $data['refresh_token'])) {
            SquareTokenManager::save($data['access_token'], $data['refresh_token']);
            return new WP_REST_Response([
                'success' => true,
                'message' => 'Token refreshed successfully',
                'data'    => ['env' => $env, 'response_code' => $code],
            ]);
        }

        return new WP_REST_Response([
            'success' => false,
            'error'   => $data['error'] ?? 'Unknown error',
            'data'    => ['env' => $env, 'response_code' => $code, 'response' => $data],
        ]);
    }

    /* ------------------------------------------------------------------ *
     * WP-Cron health check
     * ------------------------------------------------------------------ */

    private const CRON_TEST_HOOK   = 'sws_cron_health_check';
    private const CRON_TEST_OPTION = 'sws_cron_health_state';

    public function cron_test_status(): WP_REST_Response
    {
        return new WP_REST_Response($this->cron_test_payload(), 200);
    }

    public function cron_test_run(): WP_REST_Response
    {
        $now    = time();
        $run_at = $now + 30;
        $state  = [
            'started_at'    => $now,
            'scheduled_for' => $run_at,
            'fired_at'      => 0,
        ];
        update_option(self::CRON_TEST_OPTION, $state, false);

        wp_clear_scheduled_hook(self::CRON_TEST_HOOK);

        $scheduled = wp_schedule_single_event($run_at, self::CRON_TEST_HOOK);

        return new WP_REST_Response(
            array_merge(
                $this->cron_test_payload(),
                [
                    'just_scheduled' => true,
                    'schedule_ok'    => false !== $scheduled,
                ]
            ),
            200
        );
    }

    private function cron_test_payload(): array
    {
        $state = get_option(self::CRON_TEST_OPTION, []);
        if (!is_array($state)) {
            $state = [];
        }

        $now            = time();
        $started_at     = (int) ($state['started_at'] ?? 0);
        $scheduled_for  = (int) ($state['scheduled_for'] ?? 0);
        $fired_at       = (int) ($state['fired_at'] ?? 0);
        $next_scheduled = wp_next_scheduled(self::CRON_TEST_HOOK);

        $status = 'idle';
        if ($started_at && !$fired_at) {
            if ($scheduled_for && $now < $scheduled_for) {
                $status = 'pending';
            } elseif ($scheduled_for && ($now - $scheduled_for) > 600) {
                $status = 'failed';
            } else {
                $status = 'waiting';
            }
        } elseif ($fired_at && $started_at) {
            $status = 'success';
        }

        return [
            'status'            => $status,
            'started_at'        => $started_at,
            'scheduled_for'     => $scheduled_for,
            'fired_at'          => $fired_at,
            'next_scheduled'    => $next_scheduled ? (int) $next_scheduled : 0,
            'now'               => $now,
            'disable_wp_cron'   => defined('DISABLE_WP_CRON') && DISABLE_WP_CRON,
            'alternate_wp_cron' => defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON,
            'plugin_events'     => $this->upcoming_plugin_events(),
        ];
    }

    private function upcoming_plugin_events(): array
    {
        $cron = _get_cron_array();
        if (!is_array($cron)) {
            return [];
        }

        $prefixes = ['square-woo-sync', 'sws_', 'squarewoosync', 'action_scheduler'];
        $out      = [];

        foreach ($cron as $timestamp => $hooks) {
            if (!is_array($hooks)) {
                continue;
            }
            foreach ($hooks as $hook => $events) {
                $matched = false;
                foreach ($prefixes as $p) {
                    if (0 === strpos($hook, $p)) {
                        $matched = true;
                        break;
                    }
                }
                if (!$matched) {
                    continue;
                }
                $out[] = [
                    'hook'      => (string) $hook,
                    'timestamp' => (int) $timestamp,
                ];
                if (count($out) >= 25) {
                    return $out;
                }
            }
        }
        return $out;
    }

    /**
     * Hook handler for the cron health check. Stamps fired_at on the option
     * so the React poller can confirm WP-Cron actually fired.
     */
    public static function cron_test_fire(): void
    {
        $state = get_option(self::CRON_TEST_OPTION, []);
        if (!is_array($state)) {
            $state = [];
        }
        $state['fired_at'] = time();
        update_option(self::CRON_TEST_OPTION, $state, false);
    }

    /* ------------------------------------------------------------------ *
     * Debug log
     * ------------------------------------------------------------------ */

    public function log_read(WP_REST_Request $request): WP_REST_Response
    {
        $lines    = max(50, min(5000, (int) ($request->get_param('lines') ?: 200)));
        $category = strtoupper((string) $request->get_param('category'));

        $path = DebugLogger::get_log_path();
        $raw  = self::tail($path, $lines * 4);

        $categories = [];
        foreach ($raw as $l) {
            if (preg_match('/\]\s+\S+\s+\[([A-Z0-9_]+)\]/', $l, $m)) {
                $categories[$m[1]] = true;
            }
        }
        ksort($categories);

        $out = $raw;
        if ('' !== $category) {
            $out = array_values(array_filter($out, function ($l) use ($category) {
                return false !== stripos($l, '[' . $category . ']');
            }));
        }
        $out = array_slice($out, -$lines);

        return new WP_REST_Response([
            'lines'      => $out,
            'categories' => array_keys($categories),
            'size_kb'    => file_exists($path) ? round(filesize($path) / 1024, 1) : 0,
            'path'       => $path,
            'enabled'    => DebugLogger::isLoggingEnabled(),
        ]);
    }

    public function log_clear(): WP_REST_Response
    {
        DebugLogger::clear();
        return new WP_REST_Response(['success' => true, 'message' => 'Debug log cleared.']);
    }

    /* ------------------------------------------------------------------ *
     * Helpers
     * ------------------------------------------------------------------ */

    /**
     * Read the last N lines of a (potentially large) file.
     *
     * @return string[]
     */
    private static function tail(string $path, int $line_count): array
    {
        if (!file_exists($path) || !is_readable($path)) {
            return [];
        }
        $fh = @fopen($path, 'rb');
        if (!$fh) {
            return [];
        }
        $buffer = '';
        $chunk  = 8192;
        $pos    = -1;
        $lines  = 0;
        fseek($fh, 0, SEEK_END);
        $size = ftell($fh);
        while (-$pos < $size && $lines <= $line_count) {
            $read_size = min($chunk, $size + $pos + 1);
            fseek($fh, max(0, $size + $pos - $read_size + 1), SEEK_SET);
            $data    = fread($fh, $read_size);
            $buffer  = $data . $buffer;
            $lines   = substr_count($buffer, "\n");
            $pos    -= $read_size;
        }
        fclose($fh);
        $arr = preg_split("/(?<=\n)/", $buffer);
        if (!is_array($arr)) {
            return [];
        }
        if ('' === end($arr)) {
            array_pop($arr);
        }
        return array_slice($arr, -$line_count);
    }
}
