<?php
/**
 * Meta Conversions API — server-side Purchase po skutečném zaplacení.
 *
 * Proč server-side: děkovací stránka jede na vlastní šabloně bez wp_head(),
 * takže browser pixel se tam nenačte, a platby převodem se navíc páruje cronem,
 * kdy zákazník na webu vůbec není. Jediné spolehlivé místo je hook
 * elementsell_order_paid, tedy okamžik, kdy je platba fakticky připsaná.
 */

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

define( 'ELEMENTSELL_META_CAPI_API_VERSION', 'v21.0' );
define( 'ELEMENTSELL_META_CAPI_LOG_OPTION', 'elementsell_meta_capi_log' );
define( 'ELEMENTSELL_META_CAPI_LOG_LIMIT', 30 );

add_action( 'elementsell_order_saved', 'elementsell_meta_capi_capture_browser_context', 5, 1 );
add_action( 'elementsell_order_paid', 'elementsell_meta_capi_send_purchase', 40, 1 );

/**
 * Pixel ID — vlastní nastavení ElementSellu, jinak převezme z oficiálního Meta pluginu.
 *
 * @return string
 */
function elementsell_meta_capi_get_pixel_id() {
    $pixel_id = trim( (string) get_option( 'meta_pixel_id', '' ) );

    if ( $pixel_id === '' ) {
        $pixel_id = elementsell_meta_capi_get_value_from_meta_plugin(
            [
                [ 'facebook_business_extension_config', 'facebook_pixel_id' ],
                [ 'facebook_fbl4b_config', 'fbl4b_pixel_id' ],
                [ 'facebook_config', 'pixel_id' ],
            ]
        );
    }

    $pixel_id = preg_replace( '/[^0-9]/', '', (string) $pixel_id );

    return (string) apply_filters( 'elementsell_meta_capi_pixel_id', $pixel_id );
}

/**
 * Access token — stejná logika jako u pixel ID. Token se nikde nevypisuje.
 *
 * @return string
 */
function elementsell_meta_capi_get_token() {
    $token = trim( (string) get_option( 'meta_capi_token', '' ) );

    if ( $token === '' ) {
        $token = elementsell_meta_capi_get_value_from_meta_plugin(
            [
                [ 'facebook_business_extension_config', 'facebook_access_token' ],
                [ 'facebook_fbl4b_config', 'fbl4b_access_token' ],
                [ 'facebook_config', 'access_token' ],
            ]
        );
    }

    return (string) apply_filters( 'elementsell_meta_capi_token', trim( (string) $token ) );
}

/**
 * Přečte hodnotu z nastavení pluginu „Meta pixel pro WordPress", pokud tam je.
 *
 * @param array<int, array{0: string, 1: string}> $candidates Dvojice [option_name, array_key].
 * @return string
 */
function elementsell_meta_capi_get_value_from_meta_plugin( $candidates ) {
    foreach ( $candidates as $candidate ) {
        $config = get_option( $candidate[0], [] );

        if ( is_string( $config ) && $config !== '' ) {
            $decoded = json_decode( $config, true );
            $config  = is_array( $decoded ) ? $decoded : [];
        }

        if ( ! is_array( $config ) ) {
            continue;
        }

        $value = isset( $config[ $candidate[1] ] ) ? trim( (string) $config[ $candidate[1] ] ) : '';

        if ( $value !== '' ) {
            return $value;
        }
    }

    return '';
}

/**
 * @return bool
 */
function elementsell_meta_capi_is_enabled() {
    if ( get_option( 'elementsell_meta_capi_disabled' ) === '1' ) {
        return false;
    }

    return elementsell_meta_capi_get_pixel_id() !== '' && elementsell_meta_capi_get_token() !== '';
}

/**
 * Uloží k objednávce cookies a hlavičky prohlížeče, ze kterých Meta páruje uživatele.
 * Běží při zakládání objednávky — později (cron, návrat z brány) už nejsou k dispozici.
 *
 * @param int $order_id
 * @return void
 */
function elementsell_meta_capi_capture_browser_context( $order_id ) {
    $order_id = (int) $order_id;

    if ( $order_id <= 0 || ! function_exists( 'elementsell_save_order_field' ) ) {
        return;
    }

    $fbp = isset( $_COOKIE['_fbp'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['_fbp'] ) ) : '';
    $fbc = isset( $_COOKIE['_fbc'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['_fbc'] ) ) : '';

    // Referer drží původní URL i s fbclid; uložený source_page_url je normalizovaný bez query.
    $referer = isset( $_SERVER['HTTP_REFERER'] )
        ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) )
        : '';

    $source_url = isset( $_POST['source_page_url'] )
        ? esc_url_raw( wp_unslash( $_POST['source_page_url'] ) )
        : '';

    if ( $source_url === '' ) {
        $source_url = $referer;
    }

    // Když cookie _fbc chybí, ale v URL je fbclid, sestavíme _fbc podle předpisu Mety.
    if ( $fbc === '' ) {
        $fbclid = elementsell_meta_capi_extract_fbclid( $referer );

        if ( $fbclid === '' && $source_url !== '' ) {
            $fbclid = elementsell_meta_capi_extract_fbclid( $source_url );
        }

        if ( $fbclid !== '' ) {
            $fbc = 'fb.1.' . ( time() * 1000 ) . '.' . $fbclid;
        }
    }

    if ( $fbp !== '' ) {
        elementsell_save_order_field( $order_id, 'meta_fbp', $fbp );
    }

    if ( $fbc !== '' ) {
        elementsell_save_order_field( $order_id, 'meta_fbc', $fbc );
    }

    if ( $source_url !== '' ) {
        elementsell_save_order_field( $order_id, 'meta_event_source_url', $source_url );
    }

    $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] )
        ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) )
        : '';

    if ( $user_agent !== '' ) {
        elementsell_save_order_field( $order_id, 'meta_client_ua', $user_agent );
    }

    $ip = elementsell_meta_capi_get_client_ip();

    if ( $ip !== '' ) {
        elementsell_save_order_field( $order_id, 'meta_client_ip', $ip );
    }
}

/**
 * @param string $url
 * @return string
 */
function elementsell_meta_capi_extract_fbclid( $url ) {
    $query = (string) wp_parse_url( $url, PHP_URL_QUERY );

    if ( $query === '' ) {
        return '';
    }

    parse_str( $query, $params );

    return isset( $params['fbclid'] ) ? sanitize_text_field( (string) $params['fbclid'] ) : '';
}

/**
 * @return string
 */
function elementsell_meta_capi_get_client_ip() {
    $candidates = [ 'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR' ];

    foreach ( $candidates as $key ) {
        if ( empty( $_SERVER[ $key ] ) ) {
            continue;
        }

        $raw = sanitize_text_field( wp_unslash( $_SERVER[ $key ] ) );

        foreach ( explode( ',', $raw ) as $part ) {
            $ip = trim( $part );

            if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
                return $ip;
            }
        }
    }

    return '';
}

/**
 * SHA-256 podle normalizačních pravidel Mety.
 *
 * @param string $value
 * @return string
 */
function elementsell_meta_capi_hash( $value ) {
    $value = trim( (string) $value );

    if ( $value === '' ) {
        return '';
    }

    if ( function_exists( 'mb_strtolower' ) ) {
        $value = mb_strtolower( $value, 'UTF-8' );
    } else {
        $value = strtolower( $value );
    }

    return hash( 'sha256', $value );
}

/**
 * Telefon do E.164 bez plusu. České číslo bez předvolby dostane 420.
 *
 * @param string $phone
 * @return string
 */
function elementsell_meta_capi_normalize_phone( $phone ) {
    $digits = preg_replace( '/[^0-9]/', '', (string) $phone );

    if ( $digits === '' ) {
        return '';
    }

    $digits = ltrim( $digits, '0' );

    if ( strlen( $digits ) === 9 ) {
        $digits = '420' . $digits;
    }

    return $digits;
}

/**
 * Čas platby jako UNIX timestamp.
 *
 * paid_at je v DB v lokální zóně webu, kdežto PHP běžně jede v UTC — naivní
 * strtotime() proto umí vyrobit čas v budoucnosti a Meta takový event zahodí.
 * Proto parsujeme přes wp_timezone() a nikdy nevrátíme budoucnost.
 *
 * @param object $order
 * @return int
 */
function elementsell_meta_capi_get_event_time( $order ) {
    $now    = time();
    $paid_at = isset( $order->paid_at ) ? (string) $order->paid_at : '';

    if ( $paid_at === '' || $paid_at === '0000-00-00 00:00:00' ) {
        return $now;
    }

    try {
        $date = new DateTimeImmutable( $paid_at, wp_timezone() );
    } catch ( Exception $e ) {
        return $now;
    }

    $timestamp = $date->getTimestamp();

    if ( $timestamp <= 0 || $timestamp > $now ) {
        return $now;
    }

    // Meta přijímá události max 7 dní zpětně.
    if ( $timestamp < $now - ( 7 * DAY_IN_SECONDS ) ) {
        return $now - ( 6 * DAY_IN_SECONDS );
    }

    return $timestamp;
}

/**
 * Odešle Purchase. Idempotentní — jedna objednávka = jeden event.
 *
 * @param int  $order_id
 * @param bool $force Přeposlat i když už byl odeslaný (ruční test z administrace).
 * @return array{sent: bool, message: string}
 */
function elementsell_meta_capi_send_purchase( $order_id, $force = false ) {
    $order_id = (int) $order_id;

    if ( $order_id <= 0 || ! function_exists( 'elementsell_get_order' ) ) {
        return [ 'sent' => false, 'message' => 'Neplatné ID objednávky.' ];
    }

    if ( ! elementsell_meta_capi_is_enabled() ) {
        return [ 'sent' => false, 'message' => 'Meta CAPI není nastavená (chybí pixel ID nebo token).' ];
    }

    $already_sent = (string) elementsell_get_order_field( $order_id, 'meta_purchase_sent_at' );

    if ( $already_sent !== '' && ! $force ) {
        return [ 'sent' => false, 'message' => 'Už odesláno ' . $already_sent . '.' ];
    }

    // Pojistka proti souběhu (návrat z brány a cron ve stejnou chvíli).
    $lock_key = 'elementsell_meta_capi_lock_' . $order_id;

    if ( ! $force && get_transient( $lock_key ) ) {
        return [ 'sent' => false, 'message' => 'Odeslání už probíhá.' ];
    }

    set_transient( $lock_key, 1, 2 * MINUTE_IN_SECONDS );

    $order = elementsell_get_order( $order_id );

    if ( ! $order ) {
        delete_transient( $lock_key );

        return [ 'sent' => false, 'message' => 'Objednávka nenalezena.' ];
    }

    $event_id = elementsell_meta_capi_get_event_id( $order );
    $payload  = elementsell_meta_capi_build_payload( $order, $event_id );

    $response = wp_remote_post(
        'https://graph.facebook.com/' . ELEMENTSELL_META_CAPI_API_VERSION . '/'
            . elementsell_meta_capi_get_pixel_id() . '/events',
        [
            'timeout' => 15,
            'body'    => [
                'data'         => wp_json_encode( [ $payload ] ),
                'access_token' => elementsell_meta_capi_get_token(),
            ],
        ]
    );

    delete_transient( $lock_key );

    if ( is_wp_error( $response ) ) {
        $message = 'Spojení selhalo: ' . $response->get_error_message();
        elementsell_meta_capi_log( $order_id, 'error', $message );

        return [ 'sent' => false, 'message' => $message ];
    }

    $code = (int) wp_remote_retrieve_response_code( $response );
    $body = json_decode( (string) wp_remote_retrieve_body( $response ), true );

    if ( $code !== 200 ) {
        $detail = isset( $body['error']['message'] )
            ? (string) $body['error']['message']
            : 'HTTP ' . $code;

        $message = 'Meta odmítla event: ' . $detail;
        elementsell_meta_capi_log( $order_id, 'error', $message );

        return [ 'sent' => false, 'message' => $message ];
    }

    $received = isset( $body['events_received'] ) ? (int) $body['events_received'] : 0;

    elementsell_save_order_field( $order_id, 'meta_purchase_sent_at', current_time( 'mysql' ) );
    elementsell_save_order_field( $order_id, 'meta_event_id', $event_id );

    $message = 'Purchase přijat (events_received: ' . $received . ', ' . $order->amount . ' ' . $order->currency . ').';
    elementsell_meta_capi_log( $order_id, 'ok', $message );

    return [ 'sent' => true, 'message' => $message ];
}

/**
 * Stabilní ID eventu kvůli deduplikaci s případným browser pixelem.
 *
 * @param object $order
 * @return string
 */
function elementsell_meta_capi_get_event_id( $order ) {
    $uid = '';

    if ( ! empty( $order->payment_uid ) ) {
        $uid = (string) $order->payment_uid;
    } elseif ( ! empty( $order->order_uid ) ) {
        $uid = (string) $order->order_uid;
    } else {
        $uid = 'id' . (int) $order->id;
    }

    return 'es_purchase_' . $uid;
}

/**
 * @param object $order
 * @param string $event_id
 * @return array<string, mixed>
 */
function elementsell_meta_capi_build_payload( $order, $event_id ) {
    $order_id = (int) $order->id;

    $user_data = [];

    $email = isset( $order->email ) ? trim( (string) $order->email ) : '';

    if ( $email !== '' ) {
        $user_data['em']          = [ elementsell_meta_capi_hash( $email ) ];
        $user_data['external_id'] = [ elementsell_meta_capi_hash( $email ) ];
    }

    $phone = elementsell_meta_capi_normalize_phone( isset( $order->phone ) ? $order->phone : '' );

    if ( $phone !== '' ) {
        $user_data['ph'] = [ elementsell_meta_capi_hash( $phone ) ];
    }

    if ( ! empty( $order->first_name ) ) {
        $user_data['fn'] = [ elementsell_meta_capi_hash( $order->first_name ) ];
    }

    if ( ! empty( $order->surname ) ) {
        $user_data['ln'] = [ elementsell_meta_capi_hash( $order->surname ) ];
    }

    $user_data['country'] = [ elementsell_meta_capi_hash( 'cz' ) ];

    $fbp = (string) elementsell_get_order_field( $order_id, 'meta_fbp' );
    $fbc = (string) elementsell_get_order_field( $order_id, 'meta_fbc' );
    $ua  = (string) elementsell_get_order_field( $order_id, 'meta_client_ua' );
    $ip  = (string) elementsell_get_order_field( $order_id, 'meta_client_ip' );

    if ( $fbp !== '' ) {
        $user_data['fbp'] = $fbp;
    }

    if ( $fbc !== '' ) {
        $user_data['fbc'] = $fbc;
    }

    if ( $ua !== '' ) {
        $user_data['client_user_agent'] = $ua;
    }

    if ( $ip !== '' ) {
        $user_data['client_ip_address'] = $ip;
    }

    $source_url = (string) elementsell_get_order_field( $order_id, 'source_page_url' );

    if ( $source_url === '' ) {
        $source_url = (string) elementsell_get_order_field( $order_id, 'meta_event_source_url' );
    }

    if ( $source_url === '' && ! empty( $order->return_url ) ) {
        $source_url = (string) $order->return_url;
    }

    if ( $source_url === '' ) {
        $source_url = home_url( '/' );
    }

    $payload = [
        'event_name'       => 'Purchase',
        'event_time'       => elementsell_meta_capi_get_event_time( $order ),
        'event_id'         => $event_id,
        'action_source'    => 'website',
        'event_source_url' => $source_url,
        'user_data'        => $user_data,
        'custom_data'      => [
            'value'        => round( (float) $order->amount, 2 ),
            'currency'     => ! empty( $order->currency ) ? (string) $order->currency : 'CZK',
            'content_name' => (string) $order->product_name,
            'content_type' => 'product',
            'order_id'     => ! empty( $order->order_uid ) ? (string) $order->order_uid : (string) $order_id,
        ],
    ];

    $test_code = trim( (string) get_option( 'elementsell_meta_capi_test_code', '' ) );

    if ( $test_code !== '' ) {
        $payload['test_event_code'] = $test_code;
    }

    return apply_filters( 'elementsell_meta_capi_payload', $payload, $order );
}

/**
 * Log posledních pokusů — ať se příště pozná výpadek do hodiny, ne za 18 dní.
 *
 * @param int    $order_id
 * @param string $status ok|error
 * @param string $message
 * @return void
 */
function elementsell_meta_capi_log( $order_id, $status, $message ) {
    $log = get_option( ELEMENTSELL_META_CAPI_LOG_OPTION, [] );

    if ( ! is_array( $log ) ) {
        $log = [];
    }

    array_unshift(
        $log,
        [
            'time'     => current_time( 'mysql' ),
            'order_id' => (int) $order_id,
            'status'   => (string) $status,
            'message'  => (string) $message,
        ]
    );

    update_option( ELEMENTSELL_META_CAPI_LOG_OPTION, array_slice( $log, 0, ELEMENTSELL_META_CAPI_LOG_LIMIT ), false );

    if ( $status === 'error' ) {
        error_log( 'ElementSell Meta CAPI (objednávka ' . (int) $order_id . '): ' . $message );
    }
}
