<?php
/**
 * Centralised post_meta key constants and helpers.
 *
 * @package SkyEmagSync
 */

namespace SkyEmagSync;

defined( 'ABSPATH' ) || exit;

final class PostMeta {

    // ── Keys ─────────────────────────────────────────────────────────────
    const SYNC_ENABLED    = '_skyemag_sync_enabled';
    const MARGIN_PERCENT  = '_skyemag_margin_percent';
    const MIN_MARGIN_PCT  = '_skyemag_min_margin_percent';
    const MAX_MARGIN_PCT  = '_skyemag_max_margin_percent';
    const STOCK_PERCENT   = '_skyemag_stock_percent';
    const LAST_SYNC_AT   = '_skyemag_last_sync_at';
    const LAST_ERROR     = '_skyemag_last_error';

    // WooCommerce native GTIN field (WC 9.2+) – used as EAN source for eMAG.
    // This is the built-in "GTIN, UPC, EAN or ISBN" field shown under SKU.
    const EAN_SOURCE     = '_global_unique_id';

    // Set during bootstrap from product_offer/read
    const VENDOR_EXT_ID  = '_skyemag_vendor_ext_id';   // int  – eMAG offer ID
    const VAT_ID         = '_skyemag_vat_id';           // int  – eMAG vat_id
    const MIN_PRICE      = '_skyemag_min_price';        // float
    const MAX_PRICE      = '_skyemag_max_price';        // float
    const HANDLING_TIME  = '_skyemag_handling_time';    // int  – days
    const EMAG_STATUS    = '_skyemag_emag_status';      // int  – 0/1 as set on eMAG
    const EMAG_IDENTIFIER = '_skyemag_emag_identifier'; // string – part_number (SKU used for matching)

    // Publish workflow (Step 6)
    const PUBLISH_STATUS    = '_skyemag_publish_status';    // string: pending|sent|validated|rejected
    const PUBLISH_SENT_AT   = '_skyemag_publish_sent_at';   // datetime
    const PUBLISH_ERROR     = '_skyemag_publish_error';     // string
    const PUBLISH_DRY_RUN   = '_skyemag_publish_dry_run';   // json: full payload for review

    // Buy Button tracking (Step 8)
    const BUY_BUTTON_RANK   = '_skyemag_buy_button_rank';   // int: 1-4 (1 = winner)
    const NUM_OFFERS        = '_skyemag_num_offers';         // int: how many sellers
    const BEST_OFFER_PRICE  = '_skyemag_best_offer_price';  // float: cheapest ex. VAT
    const BB_UPDATED_AT     = '_skyemag_bb_updated_at';     // datetime

    // Validation status monitoring (Step 9)
    const VALIDATION_STATUS      = '_skyemag_validation_status';       // int: 0-12
    const VALIDATION_DESCRIPTION = '_skyemag_validation_description';  // string
    const VALIDATION_ERRORS      = '_skyemag_validation_errors';       // json array
    const OFFER_VALIDATION       = '_skyemag_offer_validation';        // int: 1=valid, 2=invalid price

    // Notification state flags
    const NOTIF_COMPETITOR_ALERTED = '_skyemag_notif_competitor_alerted'; // 1 while a competitor-alert is "live" for the product

    // ── Read helpers ─────────────────────────────────────────────────────

    /**
     * Get EAN/GTIN using configurable meta key sources (Settings → EAN source).
     * Tries each configured key in order, returns first non-empty value.
     */
    public static function get_ean( int $post_id ): string {
        $sources = \SkyEmagSync\Options::ean_sources();
        foreach ( $sources as $key ) {
            $val = trim( (string) get_post_meta( $post_id, $key, true ) );
            if ( '' !== $val ) {
                return $val;
            }
        }
        return '';
    }

    public static function is_sync_enabled( int $post_id ): bool {
        return (bool) get_post_meta( $post_id, self::SYNC_ENABLED, true );
    }

    /**
     * Count how many products have sync currently enabled.
     * Used by the admin UI to surface the current sync footprint to the
     * seller. There is no count-based cap on either tier.
     */
    public static function count_sync_enabled(): int {
        global $wpdb;
        return (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$wpdb->postmeta}
             WHERE meta_key = '_skyemag_sync_enabled' AND meta_value = '1'"
        );
    }

    public static function get_margin( int $post_id ): ?float {
        $val = get_post_meta( $post_id, self::MARGIN_PERCENT, true );
        return self::nullable_float( $val );
    }

    public static function get_stock_percent( int $post_id ): ?float {
        $val = get_post_meta( $post_id, self::STOCK_PERCENT, true );
        return self::nullable_float( $val );
    }

    public static function get_vendor_ext_id( int $post_id ): ?int {
        $val = get_post_meta( $post_id, self::VENDOR_EXT_ID, true );
        return ( $val !== '' && $val !== false && $val !== null ) ? (int) $val : null;
    }

    public static function get_vat_id( int $post_id ): ?int {
        $val = get_post_meta( $post_id, self::VAT_ID, true );
        return ( $val !== '' && $val !== false && $val !== null ) ? (int) $val : null;
    }

    public static function get_min_price( int $post_id ): ?float {
        $val = get_post_meta( $post_id, self::MIN_PRICE, true );
        return self::nullable_float( $val );
    }

    public static function get_max_price( int $post_id ): ?float {
        $val = get_post_meta( $post_id, self::MAX_PRICE, true );
        return self::nullable_float( $val );
    }

    public static function get_handling_time( int $post_id ): int {
        $val = get_post_meta( $post_id, self::HANDLING_TIME, true );
        return ( $val !== '' && $val !== false ) ? (int) $val : 1;
    }

    public static function get_last_sync( int $post_id ): string {
        return (string) get_post_meta( $post_id, self::LAST_SYNC_AT, true );
    }

    public static function get_last_error( int $post_id ): string {
        return (string) get_post_meta( $post_id, self::LAST_ERROR, true );
    }

    // ── Write helpers ────────────────────────────────────────────────────

    public static function set_sync_enabled( int $post_id, bool $enabled ): void {
        update_post_meta( $post_id, self::SYNC_ENABLED, (int) $enabled );
    }

    public static function get_min_margin( int $post_id ): ?float {
        $val = get_post_meta( $post_id, self::MIN_MARGIN_PCT, true );
        return self::nullable_float( $val );
    }

    public static function get_max_margin( int $post_id ): ?float {
        $val = get_post_meta( $post_id, self::MAX_MARGIN_PCT, true );
        return self::nullable_float( $val );
    }

    public static function set_min_margin( int $post_id, ?float $pct ): void {
        if ( null === $pct ) {
            delete_post_meta( $post_id, self::MIN_MARGIN_PCT );
        } else {
            update_post_meta( $post_id, self::MIN_MARGIN_PCT, $pct );
        }
    }

    public static function set_max_margin( int $post_id, ?float $pct ): void {
        if ( null === $pct ) {
            delete_post_meta( $post_id, self::MAX_MARGIN_PCT );
        } else {
            update_post_meta( $post_id, self::MAX_MARGIN_PCT, $pct );
        }
    }

    public static function set_margin( int $post_id, ?float $margin ): void {
        $old = self::get_margin( $post_id );
        if ( null === $margin ) {
            delete_post_meta( $post_id, self::MARGIN_PERCENT );
        } else {
            update_post_meta( $post_id, self::MARGIN_PERCENT, $margin );
        }
        // Fire only on actual value change. History listener records each transition.
        if ( $old !== $margin ) {
            /**
             * @param int        $post_id WC product/variation ID.
             * @param float|null $old     Previous margin, null if inherited.
             * @param float|null $margin  New margin, null if cleared.
             */
            do_action( 'skyemag_margin_changed', $post_id, $old, $margin );
        }
    }

    public static function set_stock_percent( int $post_id, ?float $pct ): void {
        if ( null === $pct ) {
            delete_post_meta( $post_id, self::STOCK_PERCENT );
        } else {
            update_post_meta( $post_id, self::STOCK_PERCENT, $pct );
        }
    }

    /**
     * Store all fields obtained from product_offer/read bootstrap in one call.
     */
    public static function store_bootstrap_data( int $post_id, array $data ): void {
        update_post_meta( $post_id, self::VENDOR_EXT_ID, (int)    $data['vendor_ext_id'] );
        update_post_meta( $post_id, self::EMAG_STATUS,   (int)    ( $data['status']       ?? 1 ) );
        update_post_meta( $post_id, self::HANDLING_TIME, (int)    ( $data['handling_time'] ?? 1 ) );
        if ( isset( $data['vat_id'] ) ) {
            update_post_meta( $post_id, self::VAT_ID, (int) $data['vat_id'] );
        }
        if ( isset( $data['min_price'] ) && $data['min_price'] > 0 ) {
            update_post_meta( $post_id, self::MIN_PRICE, (float) $data['min_price'] );
        }
        if ( isset( $data['max_price'] ) && $data['max_price'] > 0 ) {
            update_post_meta( $post_id, self::MAX_PRICE, (float) $data['max_price'] );
        }
        if ( ! empty( $data['part_number'] ) ) {
            update_post_meta( $post_id, self::EMAG_IDENTIFIER, sanitize_text_field( $data['part_number'] ) );
        }
        // Buy button data (if present in offer read response).
        if ( isset( $data['buy_button_rank'] ) ) {
            update_post_meta( $post_id, self::BUY_BUTTON_RANK, (int) $data['buy_button_rank'] );
        }
        if ( isset( $data['number_of_offers'] ) ) {
            update_post_meta( $post_id, self::NUM_OFFERS, (int) $data['number_of_offers'] );
        }
        if ( isset( $data['best_offer_sale_price'] ) ) {
            update_post_meta( $post_id, self::BEST_OFFER_PRICE, (float) $data['best_offer_sale_price'] );
        }
    }

    /**
     * Remove the eMAG identity from a product (unlink). Margin, VAT and
     * handling-time preferences are kept — only the offer identity and the
     * price bounds inherited from that offer go away. Used by the Products
     * page "Unlink from eMAG" action and the duplicate-mapping repair tool.
     */
    public static function clear_mapping( int $post_id ): void {
        delete_post_meta( $post_id, self::VENDOR_EXT_ID );
        delete_post_meta( $post_id, self::EMAG_IDENTIFIER );
        delete_post_meta( $post_id, self::EMAG_STATUS );
        delete_post_meta( $post_id, self::MIN_PRICE );
        delete_post_meta( $post_id, self::MAX_PRICE );
        delete_post_meta( $post_id, self::LAST_ERROR );
    }

    /**
     * Store buy-button tracking data from product_offer/read.
     */
    public static function store_buy_button_data( int $post_id, array $offer ): void {
        if ( isset( $offer['buy_button_rank'] ) ) {
            update_post_meta( $post_id, self::BUY_BUTTON_RANK, (int) $offer['buy_button_rank'] );
        }
        if ( isset( $offer['number_of_offers'] ) ) {
            update_post_meta( $post_id, self::NUM_OFFERS, (int) $offer['number_of_offers'] );
        }
        if ( isset( $offer['best_offer_sale_price'] ) ) {
            update_post_meta( $post_id, self::BEST_OFFER_PRICE, (float) $offer['best_offer_sale_price'] );
        }
        update_post_meta( $post_id, self::BB_UPDATED_AT, current_time( 'mysql' ) );
    }

    public static function get_buy_button_rank( int $post_id ): ?int {
        $val = get_post_meta( $post_id, self::BUY_BUTTON_RANK, true );
        return ( $val !== '' && $val !== false ) ? (int) $val : null;
    }

    public static function get_num_offers( int $post_id ): ?int {
        $val = get_post_meta( $post_id, self::NUM_OFFERS, true );
        return ( $val !== '' && $val !== false ) ? (int) $val : null;
    }

    public static function get_best_offer_price( int $post_id ): ?float {
        return self::nullable_float( get_post_meta( $post_id, self::BEST_OFFER_PRICE, true ) );
    }

    public static function get_bb_updated_at( int $post_id ): string {
        return (string) get_post_meta( $post_id, self::BB_UPDATED_AT, true );
    }

    /**
     * Store validation status data from product_offer/read.
     */
    public static function store_validation_data( int $post_id, array $offer ): void {
        // validation_status is an object: {value: int, Description: string, errors: [...]}
        $vs = $offer['validation_status'] ?? null;
        if ( is_array( $vs ) ) {
            if ( isset( $vs['value'] ) ) {
                update_post_meta( $post_id, self::VALIDATION_STATUS, (int) $vs['value'] );
            }
            if ( isset( $vs['Description'] ) ) {
                update_post_meta( $post_id, self::VALIDATION_DESCRIPTION, sanitize_text_field( $vs['Description'] ) );
            }
            if ( ! empty( $vs['errors'] ) && is_array( $vs['errors'] ) ) {
                update_post_meta( $post_id, self::VALIDATION_ERRORS, wp_json_encode( $vs['errors'] ) );
            } else {
                delete_post_meta( $post_id, self::VALIDATION_ERRORS );
            }
        }

        // offer_validation_status: {value: int, Description: string}
        $ovs = $offer['offer_validation_status'] ?? null;
        if ( is_array( $ovs ) && isset( $ovs['value'] ) ) {
            update_post_meta( $post_id, self::OFFER_VALIDATION, (int) $ovs['value'] );
        }
    }

    public static function get_validation_status( int $post_id ): ?int {
        $val = get_post_meta( $post_id, self::VALIDATION_STATUS, true );
        return ( $val !== '' && $val !== false ) ? (int) $val : null;
    }

    public static function get_validation_description( int $post_id ): string {
        return (string) get_post_meta( $post_id, self::VALIDATION_DESCRIPTION, true );
    }

    public static function get_validation_errors( int $post_id ): array {
        $raw = get_post_meta( $post_id, self::VALIDATION_ERRORS, true );
        if ( empty( $raw ) ) {
            return [];
        }
        $decoded = json_decode( $raw, true );
        return is_array( $decoded ) ? $decoded : [];
    }

    public static function get_offer_validation( int $post_id ): ?int {
        $val = get_post_meta( $post_id, self::OFFER_VALIDATION, true );
        return ( $val !== '' && $val !== false ) ? (int) $val : null;
    }

    /**
     * Human-readable label for a validation_status value.
     */
    public static function validation_label( ?int $status ): string {
        $labels = [
            0  => __( 'Draft', 'skyservers-product-sync-for-emag' ),
            1  => __( 'In MKTP validation', 'skyservers-product-sync-for-emag' ),
            2  => __( 'Awaiting Brand validation', 'skyservers-product-sync-for-emag' ),
            3  => __( 'Waiting EAN approval', 'skyservers-product-sync-for-emag' ),
            4  => __( 'Documentation pending', 'skyservers-product-sync-for-emag' ),
            5  => __( 'Rejected Brand', 'skyservers-product-sync-for-emag' ),
            6  => __( 'EAN rejected', 'skyservers-product-sync-for-emag' ),
            8  => __( 'Documentation rejected', 'skyservers-product-sync-for-emag' ),
            9  => __( 'Approved', 'skyservers-product-sync-for-emag' ),
            10 => __( 'Blocked', 'skyservers-product-sync-for-emag' ),
            11 => __( 'Update awaiting approval', 'skyservers-product-sync-for-emag' ),
            12 => __( 'Update rejected', 'skyservers-product-sync-for-emag' ),
        ];
        return $labels[ $status ] ?? __( 'Unknown', 'skyservers-product-sync-for-emag' );
    }

    /**
     * Check if a validation_status value means the product is saleable.
     */
    public static function is_validation_saleable( ?int $status ): bool {
        // Values 3, 9, 11, 12 are marked "Allowed" in eMAG docs.
        return in_array( $status, [ 3, 9, 11, 12 ], true );
    }

    public static function mark_success( int $post_id ): void {
        // Stored as GMT (second arg = true). The display layer in
        // ProductsTable converts back to the site timezone for the tooltip.
        update_post_meta( $post_id, self::LAST_SYNC_AT, current_time( 'mysql', true ) );
        delete_post_meta( $post_id, self::LAST_ERROR );
    }

    public static function mark_error( int $post_id, string $error ): void {
        update_post_meta( $post_id, self::LAST_ERROR, $error );
    }

    // ── Private ──────────────────────────────────────────────────────────

    private static function nullable_float( $val ): ?float {
        return ( $val !== '' && $val !== false && $val !== null ) ? (float) $val : null;
    }
}
