<?php
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.DirectDatabaseQuery.SchemaChange
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Abstract base class for all document types.
 * Handles data preparation, DB record management, and PDF dispatch.
 */
abstract class PBUAIV_Document {

    /** @var string Document type slug: invoice, receipt, credit_note, proforma, packing_slip, delivery_note */
    protected string $type;

    /** @var string Human-readable label */
    protected string $label;

    /** @var WC_Order */
    protected WC_Order $order;

    /** @var array Compiled document data */
    protected array $data = [];

    /** @var int|null DB record ID */
    protected ?int $doc_id = null;

    // ── Construction ───────────────────────────────────────────────────────────

    public function __construct( WC_Order $order ) {
        $this->order = $order;
        $this->data  = $this->compile();
    }

    abstract protected function get_type()  : string;
    abstract protected function get_label() : string;

    // ── Data compilation ───────────────────────────────────────────────────────

    protected function compile() : array {
        $order    = $this->order;
        $type     = $this->get_type();
        $existing = $this->get_db_record();

        $doc_number = $existing ? $existing->doc_number : '';
        $doc_date   = $existing ? $existing->doc_date   : wp_date( 'Y-m-d' );

        // If no existing record, this is a preview — don't assign number yet
        $is_issued = ! empty( $doc_number );

        // Company details
        $company = [
            'name'     => get_option( 'pbuaiv_company_name', get_bloginfo( 'name' ) ),
            'address'  => get_option( 'pbuaiv_company_address', '' ),
            'city'     => get_option( 'pbuaiv_company_city', '' ),
            'emirate'  => get_option( 'pbuaiv_company_emirate', 'Dubai' ),
            'country'  => 'UAE',
            'phone'    => get_option( 'pbuaiv_company_phone', '' ),
            'email'    => get_option( 'pbuaiv_company_email', '' ),
            'website'  => get_option( 'pbuaiv_company_website', '' ),
            'trn'      => get_option( 'pbuaiv_trn', '' ),
            'logo_url' => $this->get_logo_url(),
        ];

        // Bank details
        $bank = [
            'name'    => get_option( 'pbuaiv_bank_name', '' ),
            'iban'    => get_option( 'pbuaiv_bank_iban', '' ),
            'swift'   => get_option( 'pbuaiv_bank_swift', '' ),
            'account' => get_option( 'pbuaiv_bank_account', '' ),
        ];

        // Customer / billing
        $customer = [
            'name'     => $order->get_formatted_billing_full_name(),
            'company'  => $order->get_billing_company(),
            'email'    => $order->get_billing_email(),
            'phone'    => $order->get_billing_phone(),
            'address'  => $order->get_formatted_billing_address(),
            'trn'      => $order->get_meta( '_billing_trn', true ),
        ];

        // Determine customer type (b2b if TRN is present)
        $customer_type = ! empty( $customer['trn'] ) ? 'b2b' : 'b2c';

        // Currency and conversion
        $currency    = $order->get_currency();
        $total       = (float) $order->get_total();
        $total_tax   = (float) $order->get_total_tax();
        $subtotal    = $total - $total_tax;
        $total_aed   = PBUAIV_FTA_Compliance::to_aed( $total, $currency );
        $vat_aed     = PBUAIV_FTA_Compliance::to_aed( $total_tax, $currency );

        // Invoice type (standard / simplified)
        $invoice_type_flag = PBUAIV_FTA_Compliance::invoice_type( $total_aed, $customer_type );

        // Line items
        $items = [];
        foreach ( $order->get_items() as $item_id => $item ) {
            $product  = $item->get_product();
            $qty      = $item->get_quantity();
            $sub      = (float) $item->get_total();
            $sub_tax  = (float) $item->get_total_tax();
            $price    = $qty > 0 ? ( $sub / $qty ) : 0;
            $vat_rate = $qty > 0 ? round( ( $sub_tax / $sub ) * 100, 2 ) : get_option( 'pbuaiv_vat_rate', 5 );

            $items[] = [
                'id'            => $item_id,
                'name'          => $item->get_name(),
                'sku'           => $product ? $product->get_sku() : '',
                'qty'           => $qty,
                'price'         => round( $price, 2 ),
                'subtotal'      => round( $sub, 2 ),
                'vat'           => round( $sub_tax, 2 ),
                'vat_rate'      => $vat_rate,
                'total'         => round( $sub + $sub_tax, 2 ),
                'vat_category'  => PBUAIV_FTA_Compliance::vat_category( (float) $vat_rate ),
            ];
        }

        // Shipping
        $shipping_total = (float) $order->get_shipping_total();
        $shipping_tax   = (float) $order->get_shipping_tax();

        // Discounts
        $discount       = (float) $order->get_discount_total();

        // QR data
        $qr_data = PBUAIV_FTA_Compliance::build_qr_data(
            $company['name'],
            $company['trn'],
            $doc_date,
            $total_aed,
            $vat_aed
        );

        return apply_filters( 'pbuaiv_document_data', [
            'type'              => $type,
            'label'             => $this->get_label(),
            'doc_id'            => $this->doc_id,
            'doc_number'        => $doc_number,
            'doc_date'          => $doc_date,
            'supply_date'       => $order->get_date_completed() ? $order->get_date_completed()->date( 'Y-m-d' ) : $doc_date,
            'is_issued'         => $is_issued,
            'invoice_type'      => $invoice_type_flag,  // 'standard' or 'simplified'
            'company'           => $company,
            'bank'              => $bank,
            'customer'          => $customer,
            'customer_type'     => $customer_type,
            'order_id'          => $order->get_id(),
            'order_number'      => $order->get_order_number(),
            'order_date'        => $order->get_date_created()->date( 'Y-m-d' ),
            'order_status'      => wc_get_order_status_name( $order->get_status() ),
            'payment_method'    => $order->get_payment_method_title(),
            'currency'          => $currency,
            'items'             => $items,
            'subtotal'          => round( $subtotal, 2 ),
            'discount'          => round( $discount, 2 ),
            'shipping'          => round( $shipping_total, 2 ),
            'shipping_tax'      => round( $shipping_tax, 2 ),
            'vat_amount'        => round( $total_tax, 2 ),
            'vat_amount_aed'    => round( $vat_aed, 2 ),
            'total'             => round( $total, 2 ),
            'total_aed'         => round( $total_aed, 2 ),
            'qr_data'           => $qr_data,
            'language'          => get_option( 'pbuaiv_doc_language', 'en' ),
            'template'          => get_option( 'pbuaiv_default_template', 'modern' ),
            'brand_primary'     => get_option( 'pbuaiv_brand_primary', '#244b8e' ),
            'brand_secondary'   => get_option( 'pbuaiv_brand_secondary', '#f59e0b' ),
            'notes'             => $order->get_customer_note(),
            'custom_notes'      => get_option( 'pbuaiv_invoice_notes', '' ),
            'payment_terms'     => get_option( 'pbuaiv_payment_terms', '' ),
            'reverse_charge'    => $order->get_meta( '_reverse_charge', true ) === 'yes',
        ], $order, $type );
    }

    // ── Issue (create DB record + assign number) ────────────────────────────────

    public function issue() : bool {
        global $wpdb;

        // Check if already issued
        if ( $this->get_db_record() ) return false;

        $number = PBUAIV_Sequence::instance()->next( $this->get_type() );
        $date   = current_time( 'Y-m-d' );

        $inserted = $wpdb->insert(
            $wpdb->prefix . 'pbuaiv_documents',
            [
                'order_id'   => $this->order->get_id(),
                'doc_type'   => $this->get_type(),
                'doc_number' => $number,
                'doc_date'   => $date,
                'status'     => 'issued',
            ],
            [ '%d', '%s', '%s', '%s', '%s' ]
        );

        if ( $inserted ) {
            $this->doc_id          = $wpdb->insert_id;
            $this->data['doc_id']  = $this->doc_id;
            $this->data['doc_number'] = $number;
            $this->data['doc_date']   = $date;
            $this->data['is_issued']  = true;

            // Store on order meta for quick lookup
            $this->order->update_meta_data( "_pbuaiv_{$this->get_type()}_number", $number );
            $this->order->update_meta_data( "_pbuaiv_{$this->get_type()}_date",   $date );
            $this->order->update_meta_data( "_pbuaiv_{$this->get_type()}_id",     $this->doc_id );
            // Persist only the newly added document metadata. Calling the full
            // WC_Order::save() method from a status-change hook can re-enter
            // WooCommerce order persistence and email generation.
            $this->order->save_meta_data();

            do_action( 'pbuaiv_document_issued', $this, $this->order );
            return true;
        }
        return false;
    }

    // ── Void ───────────────────────────────────────────────────────────────────

    public function void() : bool {
        global $wpdb;
        $record = $this->get_db_record();
        if ( ! $record ) return false;
        $updated = $wpdb->update(
            $wpdb->prefix . 'pbuaiv_documents',
            [ 'status' => 'void' ],
            [ 'id' => $record->id ],
            [ '%s' ], [ '%d' ]
        );
        do_action( 'pbuaiv_document_voided', $this, $this->order );
        return (bool) $updated;
    }

    // ── Mark sent ──────────────────────────────────────────────────────────────

    public function mark_sent() : void {
        global $wpdb;
        $record = $this->get_db_record();
        if ( ! $record ) return;
        $wpdb->update(
            $wpdb->prefix . 'pbuaiv_documents',
            [ 'status' => 'sent', 'sent_at' => current_time( 'mysql' ) ],
            [ 'id' => $record->id ],
            [ '%s', '%s' ], [ '%d' ]
        );
    }

    // ── HTML rendering ────────────────────────────────────────────────────────

    public function get_html( string $template_override = '' ) : string {
        $data     = $this->data;
        $type     = $this->get_type();
        $template = $template_override ?: $data['template'];

        // Look for template file: templates/{type}/{template}.php
        $file = PBUAIV_PLUGIN_DIR . "templates/{$type}/{$template}.php";
        if ( ! file_exists( $file ) ) {
            $file = PBUAIV_PLUGIN_DIR . "templates/{$type}/modern.php";
        }
        if ( ! file_exists( $file ) ) {
            return '<p>Template not found: ' . esc_html( $file ) . '</p>';
        }

        ob_start();
        extract( $data, EXTR_SKIP );
        include $file;
        return ob_get_clean();
    }

    // ── PDF generation (used only for email attachments) ──────────────────────

    public function get_pdf() : string {
        return PBUAIV_PDF_Generator::instance()->generate( $this->get_html(), $this->data );
    }

    /**
     * Stream PDF to browser (kept for email attachment generation).
     */
    public function stream_pdf( string $mode = 'inline' ) : void {
        $pdf      = $this->get_pdf();
        $filename = $this->get_pdf_filename();

        header( 'Content-Type: application/pdf' );
        header( 'Content-Length: ' . strlen( $pdf ) );
        header( 'Content-Disposition: ' . sanitize_key( $mode ) . '; filename="' . sanitize_file_name( $filename ) . '"' );
        header( 'Cache-Control: private, max-age=0, must-revalidate' );
        header( 'Pragma: public' );
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- binary PDF output, not HTML
        echo $pdf;
        exit;
    }

    /**
     * Stream the HTML template for in-browser viewing.
     * Pixel-perfect — exactly what the preview shows.
     */
    public function stream_html_view() : void {
        $html = $this->get_html();
        header( 'Content-Type: text/html; charset=UTF-8' );
        header( 'Cache-Control: private, max-age=0, must-revalidate' );
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- full HTML document
        echo $html;
        exit;
    }

    public function get_pdf_filename() : string {
        $number = sanitize_file_name( $this->data['doc_number'] ?: $this->get_type() . '-draft' );
        return apply_filters( 'pbuaiv_pdf_filename', $this->get_type() . '-' . $number . '.pdf', $this );
    }

    // ── DB helpers ────────────────────────────────────────────────────────────

    public function get_db_record() : ?object {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}pbuaiv_documents WHERE order_id = %d AND doc_type = %s AND status != 'void' ORDER BY id DESC LIMIT 1",
            $this->order->get_id(), $this->get_type()
        ) );
    }

    public function is_issued() : bool {
        return (bool) $this->get_db_record();
    }

    // ── Getters ───────────────────────────────────────────────────────────────

    public function get_data()  : array    { return $this->data; }
    public function get_order() : WC_Order { return $this->order; }

    // ── Private helpers ───────────────────────────────────────────────────────

    private function get_logo_url() : string {
        $logo_id = (int) get_option( 'pbuaiv_logo_id', 0 );
        if ( ! $logo_id ) return '';
        $url = wp_get_attachment_image_url( $logo_id, 'full' );
        return $url ?: '';
    }
}
