<?php
/**
 * Main plugin class for Zoventic Invoices.
 *
 * @since 1.0.0
 */

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

final class Zoventic_Invoices {

    private static ?self $instance = null;

    // Singleton: block direct construction, cloning, and unserialization.
    private function __construct() {}
    private function __clone() {}

    public function __wakeup(): void {
        throw new \Exception( 'Cannot unserialize singleton.' );
    }

    public static function get_instance(): self {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function init(): void {
        if ( is_admin() ) {
            $this->init_admin();
        }

        $this->init_rest_api();
        $this->init_hooks();
    }

    private function init_admin(): void {
        if ( class_exists( 'ZINV_Admin' ) ) {
            ZINV_Admin::get_instance()->init();
        }
    }

    private function init_rest_api(): void {
        add_action( 'rest_api_init', function () {
            if ( class_exists( 'ZINV_REST_API' ) ) {
                ZINV_REST_API::register_routes();
            }
        } );
    }

    private function init_hooks(): void {
        // Run migrations on admin_init if needed.
        add_action( 'admin_init', [ $this, 'maybe_migrate' ] );
        add_action( 'admin_notices', [ $this, 'maybe_pdf_fonts_notice' ] );
        add_action( 'delete_user', [ $this, 'handle_user_deleted' ] );
        add_action( 'profile_update', [ $this, 'handle_profile_updated' ], 10, 2 );

        // Bust all dashboard transients and force summary cache refresh whenever billing data changes.
        foreach ( [
            'zinv_payment_created',
            'zinv_payment_updated',
            'zinv_payment_deleted',
            'zinv_invoice_created',
            'zinv_invoice_updated',
            'zinv_invoice_status_changed',
            'zinv_invoice_sent',
            'zinv_invoice_paid',
            'zinv_invoice_voided',
            'zinv_client_created',
            'zinv_client_updated',
            'zinv_client_archived',
            'zinv_estimate_created',
            'zinv_estimate_updated',
            'zinv_estimate_sent',
            'zinv_estimate_approved',
            'zinv_settings_saved',
        ] as $hook ) {
            add_action( $hook, [ $this, 'bust_kpi_cache' ] );
        }
    }

    public function bust_kpi_cache(): void {
        delete_transient( 'zinv_dashboard_kpis' );
        delete_transient( 'zinv_dash_monthly_revenue' );
        delete_transient( 'zinv_dash_pending_invoices' );
        if ( class_exists( 'ZINV_Summary' ) ) {
            ( new ZINV_Summary() )->refresh_all( 300, true );
        }
    }

    /**
     * Nullify wp_user_id references when a WordPress user is deleted.
     */
    public function handle_user_deleted( int $user_id ): void {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            $wpdb->prefix . 'zinv_clients',
            [ 'wp_user_id' => null ],
            [ 'wp_user_id' => $user_id ],
            [ null ],
            [ '%d' ]
        );
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    }

    /**
     * Sync name and email changes from WordPress user profile to client.
     */
    public function handle_profile_updated( int $user_id, WP_User $old_user_data ): void {
        $user = get_userdata( $user_id );
        if ( $user ) {
            global $wpdb;
            // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->update(
                $wpdb->prefix . 'zinv_clients',
                [
                    'name'  => sanitize_text_field( $user->display_name ),
                    'email' => sanitize_email( $user->user_email ),
                ],
                [ 'wp_user_id' => $user_id ],
                [ '%s', '%s' ],
                [ '%d' ]
            );
            // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        }
    }

    /**
     * Warn admins when mPDF font files are missing.
     */
    public function maybe_pdf_fonts_notice(): void {
        if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
            return;
        }

        if ( ! class_exists( 'ZINV_PDF_Generator' ) ) {
            return;
        }

        $message = ZINV_PDF_Generator::missing_fonts_message();
        if ( null === $message ) {
            return;
        }

        printf(
            '<div class="notice notice-error"><p><strong>%s</strong> %s</p></div>',
            esc_html__( 'Zoventic Invoices:', 'zoventic-invoices' ),
            esc_html( $message )
        );
    }

    public function maybe_migrate(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        $current_version = get_option( 'zinv_db_version', '0' );
        if ( version_compare( $current_version, ZINV_VERSION, '<' ) ) {
            require_once ZINV_PLUGIN_DIR . 'includes/class-installer.php';
            ZINV_Installer::maybe_migrate();
        }
    }

    public function is_pro_active(): bool {
        return (bool) apply_filters( 'zoventic_invoices_is_pro_active', false );
    }
}
