<?php
/**
 * PDP Manager: registry of connectors + transmission queue.
 *
 * Manages all registered PDP connectors and handles the transmission
 * workflow: queue → transmit → track status → retry.
 *
 * @package FacturX_For_WooCommerce
 */

defined( 'ABSPATH' ) || exit;

class GOFACTOX_PDP_Manager {

    /** @var GOFACTOX_PDP_Connector[] Registered connectors keyed by ID. */
    private static array $connectors = [];

    public static function init(): void {
        // PDP transmission is a Business-tier feature.
        if ( ! GOFACTOX_License::is_business() ) {
            return;
        }

        // Allow other plugins/addons to register connectors.
        add_action( 'gofactox_register_pdp_connectors', [ __CLASS__, 'do_registration' ], 10 );
        add_action( 'init', [ __CLASS__, 'fire_registration' ], 20 );

        // Cron for retrying failed transmissions.
        add_action( 'gofactox_retry_transmissions', [ __CLASS__, 'retry_failed' ] );

        if ( ! wp_next_scheduled( 'gofactox_retry_transmissions' ) ) {
            wp_schedule_event( time(), 'hourly', 'gofactox_retry_transmissions' );
        }
    }

    /**
     * Fire the registration hook so connectors can register themselves.
     */
    public static function fire_registration(): void {
        do_action( 'gofactox_register_pdp_connectors' );
    }

    /**
     * Placeholder for the registration hook.
     */
    public static function do_registration(): void {
        // Connectors register via: GOFACTOX_PDP_Manager::register( new My_Connector() );
    }

    /**
     * Register a PDP connector.
     */
    public static function register( GOFACTOX_PDP_Connector $connector ): void {
        self::$connectors[ $connector->get_id() ] = $connector;
    }

    /**
     * Get all registered connectors.
     *
     * @return GOFACTOX_PDP_Connector[]
     */
    public static function get_connectors(): array {
        return self::$connectors;
    }

    /**
     * Get a specific connector by ID.
     */
    public static function get_connector( string $id ): ?GOFACTOX_PDP_Connector {
        return self::$connectors[ $id ] ?? null;
    }

    /**
     * Get the active (configured) connector, if any.
     */
    public static function get_active_connector(): ?GOFACTOX_PDP_Connector {
        $active_id = get_option( 'gofactox_active_pdp_connector', '' );
        if ( ! $active_id ) {
            return null;
        }

        $connector = self::get_connector( $active_id );
        if ( $connector && $connector->is_configured() ) {
            return $connector;
        }

        return null;
    }

    /**
     * Queue an invoice for transmission.
     *
     * @param \WC_Order $order The WooCommerce order.
     * @return bool Whether the transmission was queued/sent.
     */
    public static function queue_transmission( \WC_Order $order ): bool {
        $connector = self::get_active_connector();
        if ( ! $connector ) {
            return false;
        }

        $invoice_id = $order->get_meta( '_gofactox_invoice_id' );
        if ( ! $invoice_id ) {
            return false;
        }

        // Get invoice data and files.
        $data     = GOFACTOX_Invoice::get_invoice_data( $order );
        $format   = get_option( 'gofactox_output_format', 'facturx' );
        $xml      = GOFACTOX_XML_Generator::generate( $data, $format );
        $pdf_path = GOFACTOX_Invoice::get_pdf_path( (int) $invoice_id );

        // Attempt transmission.
        try {
            $result = $connector->transmit( $data, $xml, $pdf_path );

            // Store transmission result on the order.
            $order->update_meta_data( '_gofactox_pdp_status', $result->status );
            $order->update_meta_data( '_gofactox_pdp_transmission_id', $result->transmission_id );
            $order->update_meta_data( '_gofactox_pdp_connector', $connector->get_id() );
            $order->update_meta_data( '_gofactox_pdp_last_attempt', current_time( 'mysql' ) );

            if ( ! $result->is_success() ) {
                $order->update_meta_data( '_gofactox_pdp_error', $result->message );
            } else {
                $order->delete_meta_data( '_gofactox_pdp_error' );
            }

            $order->save();

            // Log.
            if ( function_exists( 'wc_get_logger' ) ) {
                wc_get_logger()->info(
                    sprintf(
                        'PDP transmission %s for order #%d via %s: %s',
                        $result->status,
                        $order->get_id(),
                        $connector->get_name(),
                        $result->message
                    ),
                    [ 'source' => 'gofactox-e-invoicing-for-woocommerce' ]
                );
            }

            return $result->is_success();
        } catch ( \Throwable $e ) {
            $order->update_meta_data( '_gofactox_pdp_status', GOFACTOX_Transmission_Result::STATUS_ERROR );
            $order->update_meta_data( '_gofactox_pdp_error', $e->getMessage() );
            $order->save();

            if ( function_exists( 'wc_get_logger' ) ) {
                wc_get_logger()->error( 'PDP transmission failed for order #' . $order->get_id() . ': ' . $e->getMessage(), [ 'source' => 'gofactox-e-invoicing-for-woocommerce' ] );
            }
            return false;
        }
    }

    /**
     * Retry failed transmissions (called by cron).
     */
    public static function retry_failed(): void {
        $connector = self::get_active_connector();
        if ( ! $connector ) {
            return;
        }

        $orders = wc_get_orders( [
            'limit'      => 20,
            'meta_query' => [
                [
                    'key'   => '_gofactox_pdp_status',
                    'value' => [ GOFACTOX_Transmission_Result::STATUS_ERROR, GOFACTOX_Transmission_Result::STATUS_PENDING ],
                    'compare' => 'IN',
                ],
            ],
        ] );

        foreach ( $orders as $order ) {
            self::queue_transmission( $order );
        }
    }

    /**
     * Check if PDP transmission is available.
     */
    public static function is_available(): bool {
        return self::get_active_connector() !== null;
    }
}
