<?php
defined( 'ABSPATH' ) || exit;

class ChainPay_Invoice {

    /* ── Create invoice (enforces free limit + coin access) ── */
    public static function create( $order, $coin_key ) {
        global $wpdb;

        $is_pro = ChainPay_License::is_active();

        // ── 1. Coin access check ─────────────────────────────
        $coin = ChainPay_Coin_Registry::get( $coin_key );
        if ( ! $coin ) {
            return new WP_Error( 'invalid_coin', 'Invalid coin: ' . esc_html( $coin_key ) );
        }
        if ( ! $coin['free'] && ! $is_pro ) {
            return new WP_Error(
                'license_required',
                sprintf(
                    '%s requires a Pro license. <a href="%s" target="_blank">Get Pro — $49/yr →</a>',
                    esc_html( $coin['name'] ),
                    'https://chainpay.store/pricing'
                )
            );
        }

        // ── 2. Monthly payment limit check ───────────────────
        if ( ! $is_pro ) {
            $count   = ChainPay_License::get_monthly_count();
            $limit   = CHAINPAY_FREE_LIMIT;
            if ( $count >= $limit ) {
                return new WP_Error(
                    'free_limit_reached',
                    sprintf(
                        'You have used all %d free payments this month. <a href="%s" target="_blank">Upgrade to Pro for unlimited payments →</a>',
                        $limit,
                        'https://chainpay.store/pricing'
                    )
                );
            }
        }

        // ── 3. Exchange rate ──────────────────────────────────
        $fiat     = (float) $order->get_total();
        $currency = $order->get_currency();
        $rate     = ChainPay_Price_Feed::get_rate( $coin_key, $currency );

        if ( $rate <= 0.0 ) {
            return new WP_Error( 'no_rate', 'Could not fetch exchange rate for ' . esc_html( $coin_key ) . '. Please try again.' );
        }

        $decimals = isset( $coin['decimals'] ) ? (int) $coin['decimals'] : 8;
        $crypto   = ChainPay_Price_Feed::fiat_to_crypto( $fiat, $coin_key, $decimals, $currency );

        // ── 4. Derive address ─────────────────────────────────
        $address = ChainPay_xPub::derive_address( $coin_key );
        if ( is_wp_error( $address ) ) {
            return $address;
        }

        // ── 5. Save invoice ───────────────────────────────────
        $inv_id     = 'cp_' . wp_generate_password( 22, false );
        $timeout    = (int) get_option( 'chainpay_invoice_timeout', 15 );
        $expires_at = gmdate( 'Y-m-d H:i:s', time() + $timeout * 60 );
        $network    = isset( $coin['network'] ) ? $coin['network'] : '';

        $inserted = $wpdb->insert(
            $wpdb->prefix . 'chainpay_invoices',
            array(
                'order_id'      => $order->get_id(),
                'invoice_id'    => $inv_id,
                'coin'          => $coin_key,
                'network'       => $network,
                'address'       => $address,
                'amount_crypto' => $crypto,
                'amount_fiat'   => $fiat,
                'fiat_currency' => $currency,
                'exchange_rate' => $rate,
                'status'        => 'pending',
                'expires_at'    => $expires_at,
                'created_at'    => current_time( 'mysql', true ),
            ),
            array( '%d','%s','%s','%s','%s','%s','%f','%s','%f','%s','%s','%s' )
        );

        if ( ! $inserted ) {
            return new WP_Error( 'db_error', 'Failed to save invoice. Please try again.' );
        }

        // ── 6. Increment monthly usage count ─────────────────
        $new_count = ChainPay_License::increment_monthly();

        // ── 7. Store meta on WC order ─────────────────────────
        $order->update_meta_data( '_chainpay_invoice_id',    $inv_id );
        $order->update_meta_data( '_chainpay_coin',          $coin_key );
        $order->update_meta_data( '_chainpay_network',       $network );
        $order->update_meta_data( '_chainpay_address',       $address );
        $order->update_meta_data( '_chainpay_amount_crypto', $crypto );
        $order->update_meta_data( '_chainpay_amount_fiat',   $fiat );
        $order->update_meta_data( '_chainpay_exchange_rate', $rate );
        $order->update_meta_data( '_chainpay_expires_at',    $expires_at );
        $order->update_meta_data( '_chainpay_status',        'pending' );
        $order->update_meta_data( '_chainpay_plan_at_create', $is_pro ? 'pro' : 'free' );
        $order->save();

        $uri = self::build_uri( $coin, $address, $crypto );

        $order->add_order_note( sprintf(
            'ChainPay: Invoice created. Coin: %s | %s %s | Addr: %s | Expires: %s | Plan: %s | Monthly usage: %d/%s',
            $coin['name'], $crypto, $coin['symbol'], $address, $expires_at,
            $is_pro ? 'pro' : 'free',
            $new_count,
            $is_pro ? 'unlimited' : CHAINPAY_FREE_LIMIT
        ) );

        return array(
            'invoice_id'    => $inv_id,
            'address'       => $address,
            'coin'          => $coin_key,
            'coin_data'     => $coin,
            'amount_crypto' => $crypto,
            'amount_fiat'   => $fiat,
            'currency'      => $currency,
            'exchange_rate' => $rate,
            'expires_at'    => $expires_at,
            'uri'           => $uri,
            'monthly_count' => $new_count,
            'monthly_limit' => $is_pro ? 'unlimited' : CHAINPAY_FREE_LIMIT,
        );
    }

    public static function build_uri( $coin, $address, $amount ) {
        $scheme = isset( $coin['uri_scheme'] ) ? $coin['uri_scheme'] : 'bitcoin';
        return sprintf( '%s:%s?amount=%s&label=%s', $scheme, $address, $amount, rawurlencode( get_bloginfo( 'name' ) ) );
    }

    /* ── Getters ─────────────────────────────────────────────── */
    public static function get_by_order( $order_id ) {
        global $wpdb;
        return $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}chainpay_invoices WHERE order_id = %d ORDER BY created_at DESC LIMIT 1",
                $order_id
            ),
            ARRAY_A
        );
    }

    public static function get( $invoice_id ) {
        global $wpdb;
        return $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}chainpay_invoices WHERE invoice_id = %s",
                $invoice_id
            ),
            ARRAY_A
        );
    }

    /* ── Mark paid ────────────────────────────────────────────── */
    public static function mark_paid( $invoice_id, $tx_hash, $confirmations = 0 ) {
        global $wpdb;
        $inv = self::get( $invoice_id );
        if ( ! $inv ) return false;

        $coin     = ChainPay_Coin_Registry::get( $inv['coin'] );
        $required = $coin ? (int) $coin['confirms'] : 1;
        $status   = $confirmations >= $required ? 'confirmed' : 'detected';

        $wpdb->update(
            $wpdb->prefix . 'chainpay_invoices',
            array( 'status' => $status, 'tx_hash' => $tx_hash, 'confirmations' => $confirmations ),
            array( 'invoice_id' => $invoice_id ),
            array( '%s', '%s', '%d' ),
            array( '%s' )
        );

        $order = wc_get_order( $inv['order_id'] );
        if ( ! $order ) return false;

        $order->update_meta_data( '_chainpay_tx_hash',       $tx_hash );
        $order->update_meta_data( '_chainpay_confirmations', $confirmations );
        $order->update_meta_data( '_chainpay_status',        $status );
        $order->save();

        if ( 'confirmed' === $status ) {
            $order->update_status(
                (string) get_option( 'chainpay_confirmed_status', 'processing' ),
                sprintf( 'ChainPay: Confirmed. TX: %s | %s | Confs: %d', $tx_hash, $inv['coin'], $confirmations )
            );
            ChainPay_Emails::send_confirmed( $order, $inv );
        } elseif ( 'detected' === $status ) {
            $order->update_status(
                (string) get_option( 'chainpay_pending_status', 'on-hold' ),
                sprintf( 'ChainPay: Detected (unconfirmed). TX: %s | %d/%d confs', $tx_hash, $confirmations, $required )
            );
            ChainPay_Emails::send_detected( $order, $inv );
        }

        return true;
    }

    public static function mark_underpaid( $invoice_id, $received, $expected ) {
        global $wpdb;
        $inv = self::get( $invoice_id );
        if ( ! $inv ) return;

        $wpdb->update(
            $wpdb->prefix . 'chainpay_invoices',
            array( 'status' => 'underpaid' ),
            array( 'invoice_id' => $invoice_id ),
            array( '%s' ), array( '%s' )
        );

        $order = wc_get_order( $inv['order_id'] );
        if ( $order ) {
            $order->update_meta_data( '_chainpay_status', 'underpaid' );
            $order->save();
            $order->add_order_note( sprintf( 'ChainPay: Underpayment. Received: %s | Expected: %s %s', $received, $expected, $inv['coin'] ) );
            if ( 'yes' === get_option( 'chainpay_underpayment_email', 'yes' ) ) {
                ChainPay_Emails::send_underpaid( $order, $inv, $received );
            }
        }
    }

    /* ── Cron: expire + poll ─────────────────────────────────── */
    public static function cron_check() {
        global $wpdb;

        // Expire overdue invoices
        $expired = $wpdb->get_results(
            "SELECT id, order_id FROM {$wpdb->prefix}chainpay_invoices WHERE status = 'pending' AND expires_at < UTC_TIMESTAMP()",
            ARRAY_A
        );
        foreach ( $expired as $inv ) {
            $wpdb->update(
                $wpdb->prefix . 'chainpay_invoices',
                array( 'status' => 'expired' ),
                array( 'id' => $inv['id'] ),
                array( '%s' ), array( '%d' )
            );
            $order = wc_get_order( $inv['order_id'] );
            if ( $order ) {
                $order->update_meta_data( '_chainpay_status', 'expired' );
                $order->save();
                $order->update_status(
                    (string) get_option( 'chainpay_expired_status', 'cancelled' ),
                    'ChainPay: Invoice expired — no payment received.'
                );
            }
        }

        // Blockchain poll via API
        $api_key = (string) get_option( 'chainpay_api_key', '' );
        if ( '' === $api_key ) return;

        $active = $wpdb->get_results(
            "SELECT * FROM {$wpdb->prefix}chainpay_invoices WHERE status IN ('pending','detected') AND expires_at > UTC_TIMESTAMP()",
            ARRAY_A
        );
        foreach ( $active as $inv ) {
            $r = wp_remote_get(
                add_query_arg(
                    array( 'address' => $inv['address'], 'coin' => $inv['coin'], 'expected' => $inv['amount_crypto'] ),
                    CHAINPAY_API_BASE . '/payment/check'
                ),
                array( 'timeout' => 8, 'headers' => array( 'Authorization' => 'Bearer ' . $api_key ) )
            );
            if ( ! is_wp_error( $r ) ) {
                $body = json_decode( wp_remote_retrieve_body( $r ), true );
                if ( ! empty( $body['tx_hash'] ) ) {
                    self::mark_paid( $inv['invoice_id'], $body['tx_hash'], (int) ( $body['confirmations'] ?? 0 ) );
                }
            }
        }
    }

    /* ── Stats ───────────────────────────────────────────────── */
    public static function stats() {
        global $wpdb;
        $t = $wpdb->prefix . 'chainpay_invoices';
        $month_start = gmdate( 'Y-m-01 00:00:00' );
        return array(
            'total'         => (int)   $wpdb->get_var( "SELECT COUNT(*) FROM {$t}" ),
            'confirmed'     => (int)   $wpdb->get_var( "SELECT COUNT(*) FROM {$t} WHERE status='confirmed'" ),
            'pending'       => (int)   $wpdb->get_var( "SELECT COUNT(*) FROM {$t} WHERE status IN ('pending','detected')" ),
            'expired'       => (int)   $wpdb->get_var( "SELECT COUNT(*) FROM {$t} WHERE status='expired'" ),
            'revenue'       => (float) $wpdb->get_var( "SELECT COALESCE(SUM(amount_fiat),0) FROM {$t} WHERE status='confirmed'" ),
            'this_month'    => (int)   $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$t} WHERE created_at >= %s", $month_start ) ),
            'monthly_limit' => ChainPay_License::is_active() ? 'unlimited' : CHAINPAY_FREE_LIMIT,
        );
    }
}
