<?php
namespace Bookaro\Core;

defined( 'ABSPATH' ) || exit;

// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reads a sanitized request param for admin routing only; no state change, so a nonce is not required.

use Bookaro\Repository\BookingRepository;
use Bookaro\Controller\Admin\BookingAdminController;
use Bookaro\Repository\OptionRepository;
use Bookaro\Controller\Admin\SettingsPageController;
use Bookaro\Controller\Admin\SetupWizardController;
use Bookaro\Controller\Admin\GettingStartedController;
use Bookaro\Controller\Admin\SystemStatusController;
use Bookaro\Controller\Admin\ReviewNudgeController;
use Bookaro\Controller\Admin\LogsController;
use Bookaro\Controller\Admin\PrivacyController;
use Bookaro\Controller\Admin\UpgradeController;
use Bookaro\Controller\Frontend\AssetsController;
use Bookaro\Controller\Frontend\AccountEndpointsController;
use Bookaro\Controller\Frontend\ShortcodesController;
use Bookaro\Controller\Ajax\BookingController;
use Bookaro\Service\BookingService;
use Bookaro\Service\BookingActionService;
use Bookaro\Service\EmailService;
use Bookaro\Service\CronService;
use Bookaro\Service\ScheduleService;
use Bookaro\Contracts\CalendarSyncInterface;
use Bookaro\Contracts\MessagingGatewayInterface;
use Bookaro\Contracts\PaymentsGatewayInterface;
use Bookaro\Contracts\WebhookDispatcherInterface;
use Bookaro\Contracts\ReportsProviderInterface;
use Bookaro\Contracts\ExportsProviderInterface;
use Bookaro\Service\NullCalendarSync;
use Bookaro\Service\NullMessagingGateway;
use Bookaro\Service\NullPaymentsGateway;
use Bookaro\Service\NullWebhookDispatcher;
use Bookaro\Service\NullReportsProvider;
use Bookaro\Service\NullExportsProvider;
use Bookaro\Service\ServiceLocator;
use Bookaro\Service\PrivacyService;
use Bookaro\Utils\Logger;
use Bookaro\Controller\Rest\ProviderSettingsController as ProviderSettingsRestController;
use Bookaro\Controller\Ajax\ProviderSettingsController as ProviderSettingsAjaxController;
use Bookaro\Controller\Rest\AvailabilityController;
use Bookaro\Controller\Rest\UserDataController;
use Bookaro\Controller\Rest\ServicesController;
use Bookaro\Controller\Rest\ProviderServicesController;
use Bookaro\Controller\Rest\ProviderBookingsRangeController;
use Bookaro\Controller\Rest\BookingActionsController;
use Bookaro\Controller\Ajax\OwnerSettingsController;
use Bookaro\Controller\ProviderPostTypeController;
use Bookaro\Controller\ServicePostTypeController;
use Bookaro\Controller\BlocksController;

class Loader
{
    protected BookingRepository $db;
    protected ScheduleService $schedule;
    protected BookingAdminController $admin;
    protected OptionRepository $optionRepo;
    protected SettingsPageController $settingsController;
    protected SetupWizardController $setupWizardController;
    protected GettingStartedController $gettingStartedController;
    protected SystemStatusController $systemStatusController;
    protected ReviewNudgeController $reviewNudgeController;
    protected LogsController $logsController;
    protected PrivacyController $privacyController;
    protected UpgradeController $upgradeController;
    protected AssetsController $assetsController;
    protected AccountEndpointsController $accountEndpointsController;
    protected CalendarSyncInterface $calendarService;
    protected PaymentsGatewayInterface $paymentsGateway;
    protected WebhookDispatcherInterface $webhookDispatcher;
    protected ReportsProviderInterface $reportsProvider;
    protected ExportsProviderInterface $exportsProvider;
    protected ShortcodesController $shortcodesController;
    protected BookingController $bookingController;
    protected BookingService $bookingService;
    protected BookingActionService $bookingActionService;
    protected EmailService $emailService;
    protected MessagingGatewayInterface $messageService;
    protected PrivacyService $privacyService;
    protected ProviderSettingsRestController $providerSettingsController;
    protected ProviderSettingsAjaxController $providerSettingsAjaxController;
    protected AvailabilityController $availabilityController;
    protected ProviderBookingsRangeController $providerBookingsRangeController;
    protected BookingActionsController $bookingActionsController;
    protected UserDataController $userDataController;
    protected OwnerSettingsController $ownerSettingsController;
    protected ProviderPostTypeController $providerPostTypeController;
    protected ServicePostTypeController $servicePostTypeController;
    protected ServicesController $servicesController;
    protected ProviderServicesController $providerServicesController;
    protected BlocksController $blocksController;

    public function __construct()
    {
        $this->db       = new BookingRepository();
        $this->schedule = new ScheduleService( $this->db );
        $services = function_exists( 'bookaro_services' ) ? bookaro_services() : null;
        if ( $services instanceof ServiceLocator ) {
            $services->set_repository( $this->db );
            $calendar = $services->calendar( $this->db );
            $payments = $services->payments( $this->db );
            $webhooks = $services->webhooks( $this->db );
            $reports  = $services->reports( $this->db );
            $exports  = $services->exports( $this->db );
        } else {
            $calendar = new NullCalendarSync();
            $payments = new NullPaymentsGateway();
            $webhooks = new NullWebhookDispatcher();
            $reports  = new NullReportsProvider();
            $exports  = new NullExportsProvider();
        }

        $this->calendarService = $calendar instanceof CalendarSyncInterface ? $calendar : new NullCalendarSync();
        $this->paymentsGateway = $payments instanceof PaymentsGatewayInterface ? $payments : new NullPaymentsGateway();
        $this->webhookDispatcher = $webhooks instanceof WebhookDispatcherInterface ? $webhooks : new NullWebhookDispatcher();
        $this->reportsProvider = $reports instanceof ReportsProviderInterface ? $reports : new NullReportsProvider();
        $this->exportsProvider = $exports instanceof ExportsProviderInterface ? $exports : new NullExportsProvider();

        $this->emailService = new EmailService();
        if ( $services instanceof ServiceLocator ) {
            $message_gateway = $services->messaging( $this->db );
        } else {
            $message_gateway = new NullMessagingGateway();
        }
        $this->messageService = $message_gateway instanceof MessagingGatewayInterface
            ? $message_gateway
            : new NullMessagingGateway();
        $this->bookingService = new BookingService(
            $this->db,
            $this->schedule,
            $this->emailService,
            $this->calendarService,
            $this->messageService,
            $this->paymentsGateway
        );
        $this->bookingActionService = new BookingActionService( $this->bookingService, $this->db );
        $this->admin    = new BookingAdminController( $this->db, $this->bookingService );
        $this->optionRepo = new OptionRepository();
        $this->settingsController = new SettingsPageController( $this->optionRepo );
        $this->setupWizardController = new SetupWizardController( $this->optionRepo );
        $this->gettingStartedController = new GettingStartedController( $this->db, $this->optionRepo );
        $this->systemStatusController = new SystemStatusController( $this->db, $this->optionRepo );
        $this->reviewNudgeController = new ReviewNudgeController( $this->db );
        $this->logsController = new LogsController();
        $this->privacyService   = new PrivacyService( $this->db );
        $this->privacyController = new PrivacyController( $this->privacyService );
        $this->upgradeController = new UpgradeController();
        $this->assetsController = new AssetsController( $this->db );
        $this->accountEndpointsController = new AccountEndpointsController();
        $this->shortcodesController = new ShortcodesController();
        $this->bookingController = new BookingController( $this->bookingActionService );
        $this->providerSettingsController = new ProviderSettingsRestController( $this->schedule );
        $this->providerSettingsAjaxController = new ProviderSettingsAjaxController( $this->schedule );
        $this->availabilityController = new AvailabilityController( $this->schedule, $this->db );
        $this->providerBookingsRangeController = new ProviderBookingsRangeController( $this->db, $this->schedule );
        $this->bookingActionsController = new BookingActionsController( $this->bookingActionService );
        $this->userDataController = new UserDataController( $this->privacyService );
        $this->ownerSettingsController = new OwnerSettingsController();
        $this->providerPostTypeController = new ProviderPostTypeController();
        $this->servicePostTypeController = new ServicePostTypeController();
        $this->servicesController = new ServicesController();
        $this->providerServicesController = new ProviderServicesController();
        $this->blocksController = new BlocksController();

        // Register generic plugin hooks
        $this->register_hooks();
        CronService::register_hooks();

        // Bug 56: register WP-CLI fallback so admins on DISABLE_WP_CRON=true
        // hosts (or with broken system cron) can manually trigger the five
        // bookaro_* events instead of silently losing reminders, auto-complete,
        // and review invites.
        if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( '\\WP_CLI' ) ) {
            \WP_CLI::add_command( 'bookaro cron-run', [ CronService::class, 'cli_cron_run' ] );
        }

        // Register class-specific hooks
        $this->settingsController->register_hooks();
        $this->setupWizardController->register_hooks();
        $this->gettingStartedController->register_hooks();
        $this->systemStatusController->register_hooks();
        $this->reviewNudgeController->register_hooks();
        $this->logsController->register_hooks();
        $this->privacyController->register_hooks();
        $this->upgradeController->register_hooks();
        $this->assetsController->register_hooks();
        $this->accountEndpointsController->register_hooks();
        $this->shortcodesController->register_hooks();
        $this->bookingController->register_hooks();
        $this->schedule->register_hooks();
        $this->providerSettingsController->register_hooks();
        $this->providerSettingsAjaxController->register_hooks();
        $this->availabilityController->register_hooks();
        $this->providerBookingsRangeController->register_hooks();
        $this->bookingActionsController->register_hooks();
        $this->userDataController->register_hooks();
        $this->ownerSettingsController->register_hooks();
        $this->privacyService->register_hooks();
        // Bug 38: wired separately because register_hooks() short-circuits
        // on environments missing the WP privacy API. The user-delete
        // anonymisation should always run.
        $this->privacyService->register_user_delete_hook();
        $generic_mode_enabled = function_exists( 'bookaro_get_integration_mode' )
            && 'generic' === bookaro_get_integration_mode();
        if ( defined( 'BOOKARO_ENABLE_GENERIC_MODE' ) && ! BOOKARO_ENABLE_GENERIC_MODE ) {
            $generic_mode_enabled = false;
        }
        if ( $generic_mode_enabled ) {
            $this->providerPostTypeController->register_hooks();
            $this->servicePostTypeController->register_hooks();
        }
        $this->servicesController->register_hooks();
        $this->providerServicesController->register_hooks();
        $this->blocksController->register_hooks();
    }

    /**
     * Register activation, deactivation, and textdomain hooks.
     */
    protected function register_hooks(): void
    {
        add_action('admin_menu', [ $this->admin, 'register_admin_menu' ]);
        add_action('admin_menu', [ $this->admin, 'reorder_submenus' ], 999);
        add_action('admin_enqueue_scripts', [ $this, 'enqueue_admin_shared_assets' ]);
        add_action('admin_init', [ $this, 'maybe_upgrade' ]);
        add_action( 'delete_user', [ $this, 'handle_user_delete' ] );
        add_action( 'wpmu_delete_user', [ $this, 'handle_user_delete' ] );
    }

    public function handle_user_delete( int $user_id ): void
    {
        if ( ! apply_filters( 'bookaro_privacy_on_user_delete', true, $user_id ) ) {
            return;
        }
        try {
            $this->privacyService->delete_user_data( $user_id );
        } catch ( \Throwable $e ) {
            Logger::log( 'bookaro_user_delete_cleanup_failed', [
                'user_id' => $user_id,
                'exception' => $e->getMessage(),
                'code' => $e->getCode(),
            ] );
        }
    }

    public function enqueue_admin_shared_assets( string $hook ): void
    {
        if ( ! is_admin() ) {
            return;
        }

        $is_bookaro = false;
        if ( is_string( $hook ) && strpos( $hook, 'bookaro' ) !== false ) {
            $is_bookaro = true;
        }
        if ( ! $is_bookaro ) {
            $page = sanitize_key( wp_unslash( $_GET['page'] ?? '' ) );
            if ( $page !== '' && strpos( $page, 'bookaro-' ) === 0 ) {
                $is_bookaro = true;
            }
        }
        if ( ! $is_bookaro ) {
            return;
        }

        $handle = 'bookaro-admin-settings';
        if ( ! wp_style_is( $handle, 'enqueued' ) ) {
            $rel = 'assets/css/admin-settings.css';
            $path = plugin_dir_path( BOOKARO_PLUGIN_FILE ) . $rel;
            $ver = file_exists( $path ) ? (string) filemtime( $path ) : BOOKARO_VERSION;
            $url = plugins_url( $rel, BOOKARO_PLUGIN_FILE );
            wp_enqueue_style( $handle, $url, [], $ver );
        }

        $this->maybe_add_admin_theme_vars( $handle );
    }

    private function maybe_add_admin_theme_vars( string $handle ): void
    {
        static $done = false;
        if ( $done ) {
            return;
        }
        $done = true;

        $theme_defaults = [
            'accent' => '#0f766e',
            'accent_soft' => '#ccfbf1',
            'accent_text' => '#134e4a',
            'surface' => '#ffffff',
            'surface_alt' => '#f8fafc',
            'surface_muted' => '#f1f5f9',
            'border' => '#e2e8f0',
            'text' => '#0f172a',
            'muted' => '#64748b',
            'danger' => '#dc2626',
            'warning' => '#f59e0b',
            'success' => '#16a34a',
            'shadow' => 'rgba(15, 23, 42, 0.08)',
        ];
        $theme = apply_filters( 'bookaro_admin_settings_theme', $theme_defaults );
        if ( ! is_array( $theme ) ) {
            $theme = $theme_defaults;
        }

        $vars = [
            'bookaro-accent' => $this->sanitize_css_var( $theme['accent'] ?? $theme_defaults['accent'], $theme_defaults['accent'] ),
            'bookaro-accent-soft' => $this->sanitize_css_var( $theme['accent_soft'] ?? $theme_defaults['accent_soft'], $theme_defaults['accent_soft'] ),
            'bookaro-accent-text' => $this->sanitize_css_var( $theme['accent_text'] ?? $theme_defaults['accent_text'], $theme_defaults['accent_text'] ),
            'bookaro-surface' => $this->sanitize_css_var( $theme['surface'] ?? $theme_defaults['surface'], $theme_defaults['surface'] ),
            'bookaro-surface-alt' => $this->sanitize_css_var( $theme['surface_alt'] ?? $theme_defaults['surface_alt'], $theme_defaults['surface_alt'] ),
            'bookaro-surface-muted' => $this->sanitize_css_var( $theme['surface_muted'] ?? $theme_defaults['surface_muted'], $theme_defaults['surface_muted'] ),
            'bookaro-border' => $this->sanitize_css_var( $theme['border'] ?? $theme_defaults['border'], $theme_defaults['border'] ),
            'bookaro-text' => $this->sanitize_css_var( $theme['text'] ?? $theme_defaults['text'], $theme_defaults['text'] ),
            'bookaro-muted' => $this->sanitize_css_var( $theme['muted'] ?? $theme_defaults['muted'], $theme_defaults['muted'] ),
            'bookaro-danger' => $this->sanitize_css_var( $theme['danger'] ?? $theme_defaults['danger'], $theme_defaults['danger'] ),
            'bookaro-warning' => $this->sanitize_css_var( $theme['warning'] ?? $theme_defaults['warning'], $theme_defaults['warning'] ),
            'bookaro-success' => $this->sanitize_css_var( $theme['success'] ?? $theme_defaults['success'], $theme_defaults['success'] ),
            'bookaro-shadow' => $this->sanitize_css_var( $theme['shadow'] ?? $theme_defaults['shadow'], $theme_defaults['shadow'] ),
        ];
        $theme_css = '.bookaro-admin{';
        foreach ( $vars as $name => $value ) {
            if ( $value === '' ) {
                continue;
            }
            $theme_css .= '--' . $name . ':' . $value . ';';
        }
        $theme_css .= '}';
        wp_add_inline_style( $handle, $theme_css );
    }

    private function sanitize_css_var( string $value, string $fallback ): string
    {
        $value = trim( (string) $value );
        if ( $value === '' ) {
            return $fallback;
        }
        $value = preg_replace( '/[<>"\'\\\\]/', '', $value );
        return $value !== '' ? $value : $fallback;
    }

    public static function activate_static(): void
    {
        self::run_activation();
    }

    public static function deactivate_static(): void
    {
        self::run_deactivation();
    }

    /**
     * Create DB table and flush rewrite rules on activation.
     */
    public function activate(): void
    {
        self::run_activation();
    }

    /**
     * Flush rewrite rules on deactivation.
     */
    public function deactivate(): void
    {
        self::run_deactivation();
    }

    public function maybe_upgrade(): void
    {
        $installed_version = get_option( 'bookaro_version', '' );
        $installed_db_version = (int) get_option( 'bookaro_db_version', 0 );
        $target_db_version = (int) BOOKARO_DB_VERSION;

        $version_needs_upgrade = $installed_version === ''
            || version_compare( $installed_version, BOOKARO_VERSION, '<' );
        $db_needs_upgrade = $installed_db_version < $target_db_version;

        if ( ! $version_needs_upgrade && ! $db_needs_upgrade ) {
            return;
        }

        $this->db->create_table();
        if ( ! $this->db->tables_exist() ) {
            if ( class_exists( '\\Bookaro\\Utils\\Logger' ) ) {
                \Bookaro\Utils\Logger::log( 'bookaro_db_create_failed', [
                    'context' => 'upgrade',
                    'version' => BOOKARO_VERSION,
                ] );
            }
            return;
        }
        $this->db->ensure_indexes();
        $generic_mode_enabled = function_exists( 'bookaro_get_integration_mode' )
            && 'generic' === bookaro_get_integration_mode();
        if ( defined( 'BOOKARO_ENABLE_GENERIC_MODE' ) && ! BOOKARO_ENABLE_GENERIC_MODE ) {
            $generic_mode_enabled = false;
        }
        if ( $generic_mode_enabled ) {
            $this->providerPostTypeController->register_post_type();
            $this->servicePostTypeController->register_post_type();
        }
        if ( function_exists( 'bookaro_register_roles' ) ) {
            bookaro_register_roles();
        }
        if ( function_exists( 'bookaro_sync_role_caps' ) ) {
            bookaro_sync_role_caps();
        }
        flush_rewrite_rules();
        // Defer a second flush to the next non-AJAX admin request, after
        // AccountEndpointsController::add_account_endpoint() has run on `init`
        // and registered /my-account/bookings-and-management/. Without this
        // second flush the endpoint would 404 until an admin manually re-saved
        // permalinks. See bookaro_maybe_flush_rewrite_rules_after_activation().
        update_option( 'bookaro_needs_rewrite_flush', '1', false );
        CronService::schedule_events();

        update_option( 'bookaro_version', BOOKARO_VERSION );
        update_option( 'bookaro_db_version', (string) $target_db_version );
    }

    private static function run_activation(): void
    {
        $db = new BookingRepository();
        $db->create_table();
        if ( ! $db->tables_exist() ) {
            wp_die(
                esc_html__( 'MyBookaro activation failed: database tables could not be created. Please check database permissions and try again.', 'mybookaro' )
            );
        }
        $db->ensure_indexes();

        $generic_mode_enabled = function_exists( 'bookaro_get_integration_mode' )
            && 'generic' === bookaro_get_integration_mode();
        if ( defined( 'BOOKARO_ENABLE_GENERIC_MODE' ) && ! BOOKARO_ENABLE_GENERIC_MODE ) {
            $generic_mode_enabled = false;
        }
        if ( $generic_mode_enabled ) {
            $providerPostTypeController = new ProviderPostTypeController();
            $servicePostTypeController  = new ServicePostTypeController();
            $providerPostTypeController->register_post_type();
            $servicePostTypeController->register_post_type();
        }

        if ( function_exists( 'bookaro_register_roles' ) ) {
            bookaro_register_roles();
        }
        if ( function_exists( 'bookaro_sync_role_caps' ) ) {
            bookaro_sync_role_caps();
        }
        update_option( 'bookaro_version', BOOKARO_VERSION );
        update_option( 'bookaro_db_version', (string) BOOKARO_DB_VERSION );
        update_option( 'bookaro_status', 'enabled' );
        flush_rewrite_rules();
        // Defer a second flush to the next request after init=99 so the WC
        // account endpoint registered by AccountEndpointsController (which
        // hooks on `init`) gets baked into the persisted rewrite_rules. See
        // bookaro_maybe_flush_rewrite_rules_after_activation() in bookaro.php.
        update_option( 'bookaro_needs_rewrite_flush', '1', false );
        CronService::schedule_events();
    }

    private static function run_deactivation(): void
    {
        update_option( 'bookaro_status', 'disabled' );
        CronService::clear_events();
        flush_rewrite_rules();
    }
}
