<?php
/**
 * BasePage — Abstract base for all admin page scheme builders.
 *
 * PAGE CLASSES are NOT modules. They are view-layer helpers owned by a module.
 * A module instantiates its page objects in boot() or register_pages() and
 * passes $plugin to them so pages can read settings and data.
 *
 * Pages are responsible only for building the "scheme" array that the
 * RenderEngine consumes. They do not register hooks or own business logic.
 *
 * Parent–child relationship:
 *   Module → owns → Page(s) → reads → Plugin services (settings, db tables)
 *
 * @package UltraguardPro\modules\core
 */

namespace UltraguardPro\modules\core;

use UltraguardPro\Plugin;
use UltraguardPro\support\AjaxResponseBuilder;

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

abstract class BasePage {

    /** @var Plugin */
    protected Plugin $plugin;

    /** @var \UltraguardPro\modules\settings\Settings */
    protected \UltraguardPro\modules\settings\Settings $settings;

    /** @var \UltraguardPro\core\CacheManager */
    protected \UltraguardPro\core\CacheManager $cache;

    /** @var AjaxResponseBuilder */
    protected AjaxResponseBuilder $ajax_response_builder;

    /** @var string  Admin URL base */
    protected string $admin_url     = '';

    /** @var string  Dashboard URL */
    protected string $dashboard_url = '';

    /**
     * Nonce action used by create_ajax_button() for all buttons on this page.
     *
     * AjaxManager-handled pages use 'ultraguard_admin_nonce' (the default).
     * Modules with their own dedicated AJAX class (e.g. LoginLimiter, which
     * calls check_ajax_referer('ultraguard_admin_nonce')) must override
     * this to their module-specific nonce action so the JS sends the right token.
     *
     * @var string
     */
    protected string $ajax_nonce_action = 'ultraguard_admin_nonce';

    public function __construct( Plugin $plugin ) {
        $this->plugin                = $plugin;
        $this->settings              = $plugin->settings;
        $this->cache                 = $plugin->cache;
        $this->ajax_response_builder = new AjaxResponseBuilder();

        $this->admin_url     = self_admin_url( 'admin.php' );
        $this->dashboard_url = self_admin_url( 'admin.php?page=ultraguard-security-suite-dashboard' );

        // Populate handler shortcuts from the module registry
        $dashboard_module = $plugin->modules->get( 'dashboard' );
        if ( $dashboard_module && method_exists( $dashboard_module, 'get_data_handler' ) ) {
            $this->dashboard_handler = $dashboard_module->get_data_handler();
        }

        $this->init();
    }

    /** Optional post-construction hook for child pages. */
    protected function init(): void {}

    // ── Abstract ──────────────────────────────────────────────────────────────

    /**
     * Build and return the render-engine scheme for this page.
     *
     * @param  mixed $context  Optional context passed by the Admin layer.
     * @return array
     */
    abstract public function get_scheme( $context ): array;

    // ── Helpers ───────────────────────────────────────────────────────────────

    protected function get_page_url( string $page, array $params = [] ): string {
        return add_query_arg(
            array_merge( [ 'page' => 'ultraguard-security-suite-dashboard', 'ug_page' => $page ], $params ),
            $this->admin_url
        );
    }

    protected function get_setting( string $key, $default = '' ) {
        return $this->settings->get_option( $key, $default );
    }

    protected function create_nonce( string $action = 'ultraguard_admin_nonce' ): string {
        return wp_create_nonce( $action );
    }

    protected function create_ajax_button(
        string $text,
        string $action,
        array  $data    = [],
        string $style   = 'primary',
        string $icon    = '',
        string $confirm = '',
        string $nonce_action = ''
    ): array {
        $resolved_nonce_action = $nonce_action ?: $this->ajax_nonce_action;

        $btn = [
            'type'         => 'button',
            'text'         => $text,
            'button_style' => $style,
            'ajax'         => [
                'action' => $action,
                'nonce'  => $this->create_nonce( $resolved_nonce_action ),
                'data'   => $data,
            ],
        ];
        if ( $icon )    { $btn['icon']           = $icon; }
        if ( $confirm ) { $btn['ajax']['confirm'] = $confirm; }
        return $btn;
    }

    protected function create_url_button(
        string $text,
        string $url,
        string $style = 'secondary',
        string $icon  = ''
    ): array {
        $btn = [ 'type' => 'button', 'text' => $text, 'button_style' => $style, 'url' => $url ];
        if ( $icon ) { $btn['icon'] = $icon; }
        return $btn;
    }

    protected function format_file_size( int $bytes ): string {
        if ( $bytes >= 1073741824 ) { return number_format( $bytes / 1073741824, 2 ) . ' GB'; }
        if ( $bytes >= 1048576 )    { return number_format( $bytes / 1048576,    2 ) . ' MB'; }
        if ( $bytes >= 1024 )       { return number_format( $bytes / 1024,       2 ) . ' KB'; }
        return $bytes . ( $bytes === 1 ? ' byte' : ' bytes' );
    }

    /**
     * Format a UTC MySQL datetime string for display using the site's
     * WordPress timezone and the admin-configured date/time formats.
     *
     * Datetimes are stored in all plugin tables as UTC.
     * wp_date() converts the Unix timestamp to the WordPress timezone
     * (Settings → General → Timezone) automatically, so displayed times
     * always match what the admin sees in the WordPress dashboard.
     *
     * @param  string $date_string  MySQL datetime string (UTC), e.g. '2026-03-20 14:30:00'.
     * @return string               Human-readable local datetime.
     */
    protected function format_date_time( string $date_string ): string {
        if ( empty( $date_string ) || $date_string === '0000-00-00 00:00:00' ) {
            return __( 'Unknown', 'ultraguard-security-suite' );
        }

        // Parse as UTC — createFromFormat is more reliable than strtotime + ' UTC'
        // because strtotime's timezone-suffix handling varies across PHP versions.
        $dt = \DateTimeImmutable::createFromFormat(
            'Y-m-d H:i:s',
            $date_string,
            new \DateTimeZone( 'UTC' )
        );
        if ( ! $dt ) {
            return esc_html( $date_string ); // fall back to raw string if parse fails
        }
        $ts = $dt->getTimestamp();

        // Respect admin-configured formats from Settings → General.
        $date_fmt = (string) get_option( 'date_format', 'Y-m-d' );
        $time_fmt = (string) get_option( 'time_format', 'g:i a' );

        // wp_date() converts $ts (UTC Unix) → WordPress timezone for display.
        $site_today  = wp_date( 'Y-m-d' );   // current date in site timezone
        $item_date   = wp_date( 'Y-m-d', $ts );

        if ( $item_date === $site_today ) {
            // Same calendar day — show time only so "Today at 3:45 pm" is unambiguous.
            return wp_date( $time_fmt, $ts ) ?: esc_html( $date_string );
        }

        if ( $ts > ( time() - 7 * DAY_IN_SECONDS ) ) {
            // Within 7 days — short weekday + time, e.g. "Mon 3:45 pm".
            return wp_date( 'D ' . $time_fmt, $ts ) ?: esc_html( $date_string );
        }

        // Older — full date + time using admin's chosen formats.
        return wp_date( $date_fmt . ' ' . $time_fmt, $ts ) ?: esc_html( $date_string );
    }

    /**
     * Return a human-readable "time ago" string for a UTC datetime.
     *
     * The diff is computed against time() (true UTC Unix timestamp) so the
     * result is timezone-agnostic — only the absolute age of the record matters.
     * For records older than 7 days the full date is formatted using the admin's
     * date_format setting.
     *
     * @param  int|string $timestamp  Unix timestamp or UTC MySQL datetime string.
     * @return string
     */
    protected function format_time_ago( int|string $timestamp ): string {
        if ( ! $timestamp ) { return __( 'Never', 'ultraguard-security-suite' ); }

        if ( is_string( $timestamp ) ) {
            $dt = \DateTimeImmutable::createFromFormat(
                'Y-m-d H:i:s',
                $timestamp,
                new \DateTimeZone( 'UTC' )
            );
            $ts = $dt ? $dt->getTimestamp() : (int) strtotime( $timestamp );
        } else {
            $ts = $timestamp;
        }

        if ( ! $ts || $ts < 0 ) { return __( 'Unknown', 'ultraguard-security-suite' ); }

        // Use time() (UTC Unix) for a timezone-agnostic age calculation.
        $diff = time() - $ts;

        if ( $diff < 60 )     { return __( 'Just now', 'ultraguard-security-suite' ); }
        /* translators: %d: number */
        if ( $diff < 3600 )   { $m = (int) floor( $diff / 60 );   return sprintf( _n( '%d minute ago', '%d minutes ago', $m, 'ultraguard-security-suite' ), $m ); }
        /* translators: %d: number */
        if ( $diff < 86400 )  { $h = (int) floor( $diff / 3600 );  return sprintf( _n( '%d hour ago',   '%d hours ago',   $h, 'ultraguard-security-suite' ), $h ); }
        /* translators: %d: number */
        if ( $diff < 604800 ) { $d = (int) floor( $diff / 86400 ); return sprintf( _n( '%d day ago',    '%d days ago',    $d, 'ultraguard-security-suite' ), $d ); }

        // Older — show full date using admin's date_format setting via wp_date().
        return wp_date( (string) get_option( 'date_format', 'Y-m-d' ), $ts ) ?: __( 'Unknown', 'ultraguard-security-suite' );
    }

    protected function get_plugin_version(): string {
        return defined( 'ULTRAGUARD_PRO_VERSION' ) ? ULTRAGUARD_PRO_VERSION : '1.0.0';
    }

    // ── Modal & row-action helpers ─────────────────────────────────────────────
    // Used by every page that builds per-row view/edit/delete modals.

    /**
     * Wrap form-field definitions in a card, inside an INDEXED outer array.
     *
     * render_modal_html() calls render_children($scheme['content']), which
     * iterates the array and only dispatches elements with a 'type' key.
     * Passing a single associative component (e.g. ['type'=>'card','data'=>…])
     * directly as 'content' causes foreach to iterate its KEY-VALUE pairs —
     * giving 'card' (string) and [..] (no type key) — both skipped.
     * The fix: always wrap in an outer indexed array: [['type'=>'card',…]].
     *
     * @param  array<int,array> $fields  Array of form-field component schemes.
     * @return array  Indexed array ready to assign to modal 'content'.
     */
    protected function modal_content( array $fields ): array {
        return [ [ 'type' => 'card', 'data' => $fields ] ];
    }

    /**
     * Build a modal footer with a single AJAX save button.
     *
     * Also wrapped in an outer indexed array for the same render_children
     * reason as modal_content().
     *
     * @param  string               $action      AJAX action name.
     * @param  array<string,mixed>  $extra_data  Static data merged with form fields on submit.
     * @return array  Indexed array ready to assign to modal 'footer'.
     */
    protected function modal_footer_save( string $action, array $extra_data = [] ): array {
        return [ [
            'type'      => 'button-group',
            'alignment' => 'right',
            'size'      => 'medium',
            'buttons'   => [
                $this->create_ajax_button(
                    __( 'Save Changes', 'ultraguard-security-suite' ),
                    $action,
                    $extra_data,
                    'primary',
                    'dashicons-saved'
                ),
            ],
        ] ];
    }

    /**
     * Wrap per-row action buttons in a button-group component.
     *
     * Passing a plain indexed array of button schemas as the 'actions' cell
     * value causes the table renderer to output each button sequentially with
     * no wrapper — they stack vertically.  Wrapping in a button-group tells
     * ActionRenderer to output a .ultraguard-button-group flex container.
     * Sub-buttons still go through engine->render() so 'modal' properties are
     * collected correctly.
     *
     * @param  array<int,array> $buttons  Button component schemas.
     * @return array  button-group component schema.
     */
    // ── Freemium upsell helpers ───────────────────────────────────────────────

    /**
     * Build a "Pro Upsell Card" shown at the top of every Pro-locked module
     * page when the current user is on the free plan.
     *
     * Free users still see the full page structure beneath this card so they
     * understand what they are missing. The card is never shown to Pro users.
     *
     * @param  string $feature_key  Key from UpgradeManager::features()
     * @return array  card component, or [] for Pro users
     */
    protected function pro_upsell_card( string $feature_key ): array {
        if ( \UltraguardPro\support\LicenseInfo::instance()->isPro() ) {
            return [];
        }

        $feature     = \UltraguardPro\support\UpgradeManager::feature( $feature_key );
        $upgrade_url = \UltraguardPro\support\UpgradeManager::upgrade_page_url();
        $activate_url = self_admin_url( 'admin.php?page=ultraguard-security-suite-dashboard&ug_page=upgrade-manager-free' );

        if ( ! $feature ) {
            return [
                'type'  => 'card',
                'title' => __( '🔒 Pro Feature', 'ultraguard-security-suite' ),
                'class' => 'ultraguard-security-suite-upsell-card',
                'data'  => [
                    [
                        'type'       => 'alert-box',
                        'alert_type' => 'info',
                        'title'      => __( 'Upgrade to Pro to unlock this feature.', 'ultraguard-security-suite' ),
                        'message'    => '',
                        'actions'    => [
                            [ 'type' => 'button', 'text' => __( 'Upgrade to Pro →', 'ultraguard-security-suite' ), 'button_style' => 'primary', 'url' => $upgrade_url ],
                        ],
                    ],
                ],
            ];
        }

        $benefit_items = array_map( static fn( string $b ): array => [
            'icon' => 'dashicons-yes-alt',
            'text' => $b,
        ], $feature['benefits'] ?? [] );

        return [
            'type'  => 'card',
            'title' => /* translators: %1$s: feature name, %2$s: 'Pro Feature' label */
            sprintf( '🔒 %1$s — %2$s', esc_html( $feature['label'] ), esc_html( __( 'Pro Feature', 'ultraguard-security-suite' ) ) ),
            'icon'  => $feature['icon'] ?? 'dashicons-lock',
            'class' => 'ultraguard-security-suite-upsell-card',
            'data'  => array_filter( [
                [
                    'type'       => 'alert-box',
                    'alert_type' => 'info',
                    'title'      => $feature['headline'],
                    'message'    => $feature['description'],
                ],
                ! empty( $benefit_items ) ? [
                    'type'  => 'list',
                    'items' => $benefit_items,
                ] : [],
                [
                    'type'      => 'button-group',
                    'alignment' => 'left',
                    'buttons'   => [
                        [
                            'type'         => 'button',
                            'text'         => __( 'Upgrade to Pro →', 'ultraguard-security-suite' ),
                            'button_style' => 'primary',
                            'url'          => $upgrade_url,
                        ],
                        [
                            'type'         => 'button',
                            'text'         => __( 'See All Pro Features', 'ultraguard-security-suite' ),
                            'button_style' => 'secondary',
                            'url'          => $upgrade_url,
                        ],
                        [
                            'type'         => 'button',
                            'text'         => __( 'Activate Existing License →', 'ultraguard-security-suite' ),
                            'button_style' => 'link',
                            'url'          => $activate_url,
                        ],
                    ],
                ],
            ] ),
        ];
    }

    /**
     * Build a warning-style retention-limit hint for modules with capped data
     * history on the free plan (audit log, traffic monitor).
     *
     * @param  string $message      Human-readable retention explanation.
     * @param  string $feature_key  Key from UpgradeManager::features()
     * @return array  alert-box component, or [] for Pro users
     */
    protected function pro_retention_hint( string $message, string $feature_key ): array {
        if ( \UltraguardPro\support\LicenseInfo::instance()->isPro() ) {
            return [];
        }

        $upgrade_url = \UltraguardPro\support\UpgradeManager::upgrade_page_url();

        return [
            'type'       => 'alert-box',
            'alert_type' => 'warning',
            'title'      => __( '⏳ Free Plan Data Limit', 'ultraguard-security-suite' ),
            'message'    => $message,
            'actions'    => [
                [
                    'type'         => 'button',
                    'text'         => __( 'Upgrade to Pro for Full History →', 'ultraguard-security-suite' ),
                    'button_style' => 'secondary',
                    'url'          => $upgrade_url,
                ],
            ],
        ];
    }

    /**
     * Prepend a premium-module notice to a module page when the current
     * license tier does not include that module.
     *
     * Soft-gated modules still render normally for free users, but the page
     * should clearly state that write actions are disabled until the upgrade.
     *
     * @param string $module_id    Registered module slug.
     * @param string $module_name  Human-readable module name.
     * @param array  $data         Existing page data array.
     * @return array
     */
    protected function prepend_soft_gated_module_notice( string $module_id, string $module_name, array $data ): array {
        $manager = $this->plugin->modules->get_module_manager();

        if ( ! $manager ) {
            return $data;
        }

        $modules = $manager->get_all_modules( true );
        $module  = $modules[ $module_id ] ?? null;

        if ( ! is_array( $module ) ) {
            return $data;
        }

        $license_required = (string) ( $module['license_required'] ?? 'free' );
        $license_ok       = (bool) ( $module['license_ok'] ?? true );

        if ( 'free' === $license_required || $license_ok ) {
            return $data;
        }

        array_unshift(
            $data,
            [
                'type'       => 'alert-box',
                'alert_type' => 'warning',
                'title'      => sprintf(
                    /* translators: %s: module name */
                    __( '%s is a Premium Module', 'ultraguard-security-suite' ),
                    $module_name
                ),
                'message'    => sprintf(
                    /* translators: 1: module name, 2: required license tier */
                    __( 'You are viewing the %1$s dashboard in preview mode. Sample data is shown, and saving changes or running mutating actions is disabled until you upgrade to %2$s.', 'ultraguard-security-suite' ),
                    $module_name,
                    strtoupper( $license_required )
                ),
                'actions'    => [
                    [
                        'type'         => 'button',
                        'text'         => __( 'Upgrade to Pro', 'ultraguard-security-suite' ),
                        'button_style' => 'primary',
                        'url'          => $manager->get_upgrade_url(),
                    ],
                ],
            ]
        );

        return $data;
    }

    /**
     * Whether a module page should show preview/sample data instead of live
     * production data because the current license does not include it.
     */
    protected function is_soft_gated_module_preview( string $module_id ): bool {
        $manager = $this->plugin->modules->get_module_manager();

        if ( ! $manager ) {
            return false;
        }

        $modules = $manager->get_all_modules( true );
        $module  = $modules[ $module_id ] ?? null;

        if ( ! is_array( $module ) ) {
            return false;
        }

        return 'free' !== (string) ( $module['license_required'] ?? 'free' )
            && empty( $module['license_ok'] );
    }

    /**
     * Standard preview-data notice used by soft-gated premium module pages.
     */
    protected function soft_gated_preview_data_notice( string $module_name, string $message = '' ): array {
        return [
            'type'       => 'alert-box',
            'alert_type' => 'info',
            'title'      => __( 'Preview Data Shown In Free Mode', 'ultraguard-security-suite' ),
            'message'    => $message ?: sprintf(
                /* translators: %s: module name */
                __( '%s is currently showing curated sample data so you can explore the workflow safely. Upgrade to unlock live data, real scans, and save actions.', 'ultraguard-security-suite' ),
                $module_name
            ),
        ];
    }

    protected function row_actions( array $buttons ): array {
        $normalized_buttons = array_map(
            static function( $button ) {
                if ( ! is_array( $button ) ) {
                    return $button;
                }

                $button = wp_parse_args(
                    $button,
                    [
                        'text'      => '',
                        'icon'      => '',
                        'icon_only' => false,
                    ]
                );

                if ( '' === trim( (string) $button['text'] ) && ! empty( $button['icon'] ) ) {
                    $button['icon_only'] = true;
                }

                return $button;
            },
            $buttons
        );

        return [
            'type'      => 'button-group',
            'size'      => 'mini',
            'alignment' => 'right',
            'class'     => 'ultraguard-row-actions',
            'buttons'   => $normalized_buttons,
        ];
    }
}
