<?php

namespace WPSN\Modules\Privacy;

use WPSN\Core\AbstractModule;
use WPSN\Modules\Privacy\Admin\PrivacyAdmin;

defined('ABSPATH') || exit;

/**
 * Trust & Privacy Pack
 *
 * - Local-only mode toggle (disables external services at runtime)
 * - External request audit log
 * - Data map (transparency) in Tools
 */
final class PrivacyModule extends AbstractModule
{
    public function id(): string
    {
        return 'privacy';
    }

    public function name(): string
    {
        return __('Privacy & Data Map', 'wp-seo-next');
    }

    public function description(): string
    {
        return __('Local-only mode, transparency data map, and external request audit log.', 'wp-seo-next');
    }

    public function defaultEnabled(): bool
    {
        return true;
    }

    public function register(): void
    {
        // DB installer.
        add_action('init', [PrivacyInstaller::class, 'maybeInstall'], 1);

        // Admin UI.
        if (is_admin()) {
            (new PrivacyAdmin($this->container))->register();
        }

        // External request audit log (best-effort, only when marked).
        add_action('http_api_debug', [$this, 'onHttpApiDebug'], 10, 5);

        // Scheduled purge.
        add_action('init', [$this, 'maybeSchedulePurge'], 1);
        add_action('wpsn_privacy_purge_external_log', [$this, 'purgeLogs']);
    }

    public function maybeSchedulePurge(): void
    {
        // Only schedule if logging is enabled.
        if (!(bool) $this->settings->get('privacy.log_external_requests', true)) {
            return;
        }

        if (!function_exists('wp_next_scheduled')) {
            return;
        }

        if (!wp_next_scheduled('wpsn_privacy_purge_external_log')) {
            wp_schedule_event(time() + 300, 'daily', 'wpsn_privacy_purge_external_log');
        }
    }

    public function purgeLogs(): void
    {
        if (!(bool) $this->settings->get('privacy.log_external_requests', true)) {
            return;
        }

        $days = (int) $this->settings->get('privacy.retention_days', 30);
        $days = max(1, min(365, $days));

        $repo = new ExternalRequestLogRepository();
        $repo->purgeOlderThan($days);
    }

    /**
     * Hook: http_api_debug
     *
     * @param mixed $response
     * @param string $context
     * @param string $class
     * @param array<string,mixed> $args
     * @param string $url
     */
    public function onHttpApiDebug($response, string $context, string $class, array $args, string $url): void
    {
        // Keep it very cheap.
        if ($context !== 'response') {
            return;
        }

        if (!(bool) $this->settings->get('privacy.log_external_requests', true)) {
            return;
        }

        $headers = $args['headers'] ?? [];
        if (!is_array($headers)) {
            $headers = [];
        }

        $service = '';
        $ctx = '';

        foreach ($headers as $hk => $hv) {
            $hkLower = strtolower((string) $hk);
            if ($hkLower === 'x-wpsn-service') {
                $service = is_array($hv) ? (string) reset($hv) : (string) $hv;
            }
            if ($hkLower === 'x-wpsn-context') {
                $ctx = is_array($hv) ? (string) reset($hv) : (string) $hv;
            }
        }

        $service = sanitize_key($service);
        $ctx = sanitize_key($ctx);

        if ($service === '') {
            return; // we only log marked requests
        }

        $method = isset($args['method']) ? strtoupper(sanitize_key((string) $args['method'])) : 'GET';

        $requestBytes = 0;
        if (isset($args['body'])) {
            if (is_string($args['body'])) {
                $requestBytes = strlen($args['body']);
            } elseif (is_array($args['body'])) {
                $json = wp_json_encode($args['body']);
                if (is_string($json)) {
                    $requestBytes = strlen($json);
                }
            }
        }

        $code = 0;
        $err = '';

        if (is_wp_error($response)) {
            $err = $response->get_error_message();
        } elseif (is_array($response)) {
            $code = (int) wp_remote_retrieve_response_code($response);
            if ($code >= 400) {
                $body = (string) wp_remote_retrieve_body($response);
                $err = $body !== '' ? $body : ('HTTP ' . $code);
            }
        }

        $safeUrl = $this->stripQueryAndFragment($url);

        try {
            $repo = new ExternalRequestLogRepository();
            $repo->insert([
                'service' => $service,
                'context' => $ctx,
                'method' => $method,
                'url' => $safeUrl,
                'response_code' => $code,
                'error' => $err,
                'request_bytes' => $requestBytes,
                'created_at' => gmdate('Y-m-d H:i:s'),
            ]);
        } catch (\Throwable $e) {
            // Never break page rendering.
            $this->logger->debug('External log insert failed: ' . $e->getMessage());
        }
    }

    private function stripQueryAndFragment(string $url): string
    {
        if (!function_exists('wp_parse_url')) {
            return $url;
        }

        $parts = wp_parse_url($url);
        if (!is_array($parts)) {
            return $url;
        }

        $scheme = isset($parts['scheme']) ? (string) $parts['scheme'] : '';
        $host = isset($parts['host']) ? (string) $parts['host'] : '';
        $port = isset($parts['port']) ? (int) $parts['port'] : 0;
        $path = isset($parts['path']) ? (string) $parts['path'] : '';

        if ($scheme === '' || $host === '') {
            return $url;
        }

        $out = $scheme . '://' . $host;
        if ($port > 0 && !in_array($port, [80, 443], true)) {
            $out .= ':' . $port;
        }
        $out .= $path;

        return $out;
    }
}
