<?php
/**
 * REST API endpoints for invoices and credit notes.
 *
 * Routes (all under /wp-json/gofactox/v1):
 *   GET  /invoices                        List invoices (paginated, filterable by date)
 *   GET  /invoices/{id}                   Get one invoice (metadata + download URL)
 *   GET  /invoices/{id}/pdf               Download the PDF/A-3 file
 *   GET  /credit-notes                    List credit notes (paginated)
 *   GET  /credit-notes/{id}               Get one credit note
 *   GET  /credit-notes/{id}/pdf           Download the PDF
 *
 * Authentication uses standard WordPress + WooCommerce mechanisms:
 *   - WP application passwords
 *   - WC REST API keys (Basic auth)
 *   - Cookie auth + nonce (for first-party JS)
 *
 * @package FacturX_For_WooCommerce
 */

defined( 'ABSPATH' ) || exit;

class GOFACTOX_Rest_Api {

    private const NAMESPACE = 'gofactox/v1';

    public static function init(): void {
        add_action( 'rest_api_init', [ __CLASS__, 'register_routes' ] );

        // Register webhook topics so users can subscribe to gofactox.* events
        // from WooCommerce > Settings > Advanced > Webhooks.
        add_filter( 'woocommerce_webhook_topics', [ __CLASS__, 'register_webhook_topics' ] );
        add_filter( 'woocommerce_valid_webhook_actions', [ __CLASS__, 'register_webhook_actions' ] );
        add_filter( 'woocommerce_webhook_topic_hooks', [ __CLASS__, 'register_webhook_topic_hooks' ] );
    }

    public static function register_routes(): void {
        register_rest_route( self::NAMESPACE, '/invoices', [
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => [ __CLASS__, 'list_invoices' ],
            'permission_callback' => [ __CLASS__, 'permission_read' ],
            'args'                => [
                'per_page' => [ 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100 ],
                'page'     => [ 'type' => 'integer', 'default' => 1, 'minimum' => 1 ],
                'after'    => [ 'type' => 'string', 'format' => 'date' ],
                'before'   => [ 'type' => 'string', 'format' => 'date' ],
            ],
        ] );

        register_rest_route( self::NAMESPACE, '/invoices/(?P<id>\d+)', [
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => [ __CLASS__, 'get_invoice' ],
            'permission_callback' => [ __CLASS__, 'permission_read' ],
        ] );

        register_rest_route( self::NAMESPACE, '/invoices/(?P<id>\d+)/pdf', [
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => [ __CLASS__, 'download_invoice_pdf' ],
            'permission_callback' => [ __CLASS__, 'permission_read' ],
        ] );

        register_rest_route( self::NAMESPACE, '/credit-notes', [
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => [ __CLASS__, 'list_credit_notes' ],
            'permission_callback' => [ __CLASS__, 'permission_read' ],
            'args'                => [
                'per_page' => [ 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100 ],
                'page'     => [ 'type' => 'integer', 'default' => 1, 'minimum' => 1 ],
                'after'    => [ 'type' => 'string', 'format' => 'date' ],
                'before'   => [ 'type' => 'string', 'format' => 'date' ],
            ],
        ] );

        register_rest_route( self::NAMESPACE, '/credit-notes/(?P<id>\d+)', [
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => [ __CLASS__, 'get_credit_note' ],
            'permission_callback' => [ __CLASS__, 'permission_read' ],
        ] );

        register_rest_route( self::NAMESPACE, '/credit-notes/(?P<id>\d+)/pdf', [
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => [ __CLASS__, 'download_credit_note_pdf' ],
            'permission_callback' => [ __CLASS__, 'permission_read' ],
        ] );
    }

    /**
     * Require WooCommerce-level read permission AND a Pro license.
     *
     * The REST API is a Pro feature: most competitors (Tyche, e-facturX, WPOvernight)
     * gate API access behind the first paid tier. Listing routes still register so
     * the OPTIONS preflight works; only the data callbacks are gated.
     */
    public static function permission_read(): bool {
        if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'read_private_shop_orders' ) ) {
            return false;
        }
        if ( ! class_exists( 'GOFACTOX_License' ) || ! GOFACTOX_License::is_pro() ) {
            return new \WP_Error(
                'gofactox_pro_required',
                __( 'The GoFactoX REST API is available on the Pro and Business plans.', 'gofactox-e-invoicing-for-woocommerce' ),
                [ 'status' => 402 ]
            );
        }
        return true;
    }

    public static function list_invoices( \WP_REST_Request $request ): \WP_REST_Response {
        return self::list_posts( $request, false );
    }

    public static function list_credit_notes( \WP_REST_Request $request ): \WP_REST_Response {
        return self::list_posts( $request, true );
    }

    private static function list_posts( \WP_REST_Request $request, bool $credit_notes ): \WP_REST_Response {
        $args = [
            'post_type'      => GOFACTOX_Invoice::POST_TYPE,
            'post_status'    => 'publish',
            'posts_per_page' => (int) $request->get_param( 'per_page' ),
            'paged'          => (int) $request->get_param( 'page' ),
            'meta_query'     => [
                [
                    'key'     => '_gofactox_is_credit_note',
                    'value'   => $credit_notes ? '1' : '0',
                    'compare' => '=',
                ],
            ],
            'orderby'        => 'date',
            'order'          => 'DESC',
        ];

        $after  = $request->get_param( 'after' );
        $before = $request->get_param( 'before' );
        if ( $after || $before ) {
            $date_query = [];
            if ( $after ) {
                $date_query['after'] = $after;
            }
            if ( $before ) {
                $date_query['before'] = $before;
            }
            $args['date_query'] = [ $date_query ];
        }

        $query = new \WP_Query( $args );
        $items = [];
        foreach ( $query->posts as $post ) {
            $items[] = self::format_post( $post );
        }

        $response = new \WP_REST_Response( $items );
        $response->header( 'X-WP-Total', (string) $query->found_posts );
        $response->header( 'X-WP-TotalPages', (string) $query->max_num_pages );
        return $response;
    }

    public static function get_invoice( \WP_REST_Request $request ): \WP_REST_Response {
        return self::get_one( (int) $request['id'], false );
    }

    public static function get_credit_note( \WP_REST_Request $request ): \WP_REST_Response {
        return self::get_one( (int) $request['id'], true );
    }

    private static function get_one( int $id, bool $expect_credit_note ): \WP_REST_Response {
        $post = get_post( $id );
        if ( ! $post || $post->post_type !== GOFACTOX_Invoice::POST_TYPE ) {
            return new \WP_REST_Response( [ 'code' => 'not_found', 'message' => 'Not found' ], 404 );
        }

        $is_cn = GOFACTOX_Invoice::is_credit_note( $id );
        if ( $is_cn !== $expect_credit_note ) {
            return new \WP_REST_Response( [ 'code' => 'wrong_type', 'message' => 'Wrong document type for this endpoint' ], 404 );
        }

        return new \WP_REST_Response( self::format_post( $post ) );
    }

    public static function download_invoice_pdf( \WP_REST_Request $request ) {
        return self::stream_pdf( (int) $request['id'], false );
    }

    public static function download_credit_note_pdf( \WP_REST_Request $request ) {
        return self::stream_pdf( (int) $request['id'], true );
    }

    private static function stream_pdf( int $id, bool $expect_credit_note ) {
        $post = get_post( $id );
        if ( ! $post || $post->post_type !== GOFACTOX_Invoice::POST_TYPE ) {
            return new \WP_REST_Response( [ 'code' => 'not_found', 'message' => 'Not found' ], 404 );
        }

        if ( GOFACTOX_Invoice::is_credit_note( $id ) !== $expect_credit_note ) {
            return new \WP_REST_Response( [ 'code' => 'wrong_type', 'message' => 'Wrong document type for this endpoint' ], 404 );
        }

        $pdf_path = GOFACTOX_Invoice::get_pdf_path( $id );
        if ( ! $pdf_path || ! file_exists( $pdf_path ) ) {
            return new \WP_REST_Response( [ 'code' => 'file_missing', 'message' => 'PDF file not found' ], 404 );
        }

        $number = (string) get_post_meta( $id, '_gofactox_invoice_number', true );
        $filename = sanitize_file_name( $number !== '' ? $number : ( $expect_credit_note ? 'credit-note' : 'invoice' ) ) . '.pdf';

        header( 'Content-Type: application/pdf' );
        header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
        header( 'Content-Length: ' . filesize( $pdf_path ) );
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
        readfile( $pdf_path );
        exit;
    }

    /**
     * Serialize an invoice / credit note post for the API response.
     */
    private static function format_post( \WP_Post $post ): array {
        $id = $post->ID;
        return [
            'id'                  => $id,
            'number'              => (string) get_post_meta( $id, '_gofactox_invoice_number', true ),
            'date'                => (string) get_post_meta( $id, '_gofactox_invoice_date', true ),
            'is_credit_note'      => GOFACTOX_Invoice::is_credit_note( $id ),
            'order_id'            => (int) get_post_meta( $id, '_gofactox_order_id', true ),
            'refund_id'           => (int) get_post_meta( $id, '_gofactox_refund_id', true ) ?: null,
            'original_invoice_id' => (int) get_post_meta( $id, '_gofactox_original_invoice_id', true ) ?: null,
            'currency'            => (string) get_post_meta( $id, '_gofactox_currency', true ),
            'total_ht'            => (float) get_post_meta( $id, '_gofactox_total_ht', true ),
            'total_tax'           => (float) get_post_meta( $id, '_gofactox_total_tax', true ),
            'total_ttc'           => (float) get_post_meta( $id, '_gofactox_total_ttc', true ),
            'pdf_url'             => rest_url( self::NAMESPACE . '/' . ( GOFACTOX_Invoice::is_credit_note( $id ) ? 'credit-notes' : 'invoices' ) . '/' . $id . '/pdf' ),
            'created_at'          => mysql_to_rfc3339( $post->post_date_gmt ),
        ];
    }

    /**
     * Add GoFactoX topics to the WooCommerce webhook dropdown.
     */
    public static function register_webhook_topics( array $topics ): array {
        $topics['gofactox.invoice.created']        = __( 'GoFactoX: Invoice created', 'gofactox-e-invoicing-for-woocommerce' );
        $topics['gofactox.invoice.regenerated']    = __( 'GoFactoX: Invoice regenerated', 'gofactox-e-invoicing-for-woocommerce' );
        $topics['gofactox.credit_note.created']    = __( 'GoFactoX: Credit note created', 'gofactox-e-invoicing-for-woocommerce' );
        $topics['gofactox.credit_note.regenerated']= __( 'GoFactoX: Credit note regenerated', 'gofactox-e-invoicing-for-woocommerce' );
        return $topics;
    }

    /**
     * Mark gofactox action names as valid trigger sources for WC webhooks.
     */
    public static function register_webhook_actions( array $actions ): array {
        return array_merge( $actions, [
            'gofactox_invoice_created',
            'gofactox_invoice_regenerated',
            'gofactox_credit_note_created',
            'gofactox_credit_note_regenerated',
        ] );
    }

    /**
     * Bind GoFactoX topic strings to the underlying do_action hooks.
     */
    public static function register_webhook_topic_hooks( array $topic_hooks ): array {
        $topic_hooks['gofactox.invoice.created']         = [ 'gofactox_invoice_created' ];
        $topic_hooks['gofactox.invoice.regenerated']     = [ 'gofactox_invoice_regenerated' ];
        $topic_hooks['gofactox.credit_note.created']     = [ 'gofactox_credit_note_created' ];
        $topic_hooks['gofactox.credit_note.regenerated'] = [ 'gofactox_credit_note_regenerated' ];
        return $topic_hooks;
    }
}
