<?php
namespace WebshoptigerCatalogSync;

defined( 'ABSPATH' ) || exit;

/**
 * BrandSync
 *
 * Syncs brands to destination sites.
 *
 * Strategy (in order):
 *  1. WooCommerce brands REST endpoint  — if a WC brands plugin is present
 *  2. WordPress taxonomy REST API       — if product_brand has show_in_rest=true
 *  3. WooCommerce global attribute      — always available, visible on the product page
 *
 * No plugin required on the destination site.
 */
class BrandSync {

    const TAXONOMY  = 'product_brand';
    const ATTR_SLUG = 'brand'; // slug of the WooCommerce global attribute on the destination site

    // -------------------------------------------------------------------------
    // Main method: ensure brands exist on the destination site and return remote IDs
    // -------------------------------------------------------------------------

    public static function resolve( int $product_id, array $site ): array {
        $terms = wp_get_object_terms( $product_id, self::TAXONOMY );
        if ( is_wp_error( $terms ) || empty( $terms ) ) {
            return [];
        }

        $creds      = self::creds( $site );
        $remote_ids = [];

        foreach ( $terms as $term ) {
            $remote_id = self::ensure_term( $term, $site, $creds );
            if ( $remote_id ) {
                $remote_ids[] = $remote_id;
            }
        }

        return $remote_ids;
    }

    // -------------------------------------------------------------------------
    // Look up or create the term on the destination site
    // -------------------------------------------------------------------------

    private static function ensure_term( \WP_Term $term, array $site, string $creds ): ?int {
        // Check the persistent cache
        $cached = self::get_cached_id( $site['id'], $term->term_id );
        if ( $cached ) {
            return $cached;
        }

        // Not in cache — look it up on the destination site
        $id = self::find_wc_brands( $term->slug, $site, $creds );

        // Fallback: WP taxonomy REST API
        if ( ! $id ) {
            $id = self::find_wp_rest( $term->slug, $site, $creds );
        }

        // Still not found — create it
        if ( ! $id ) {
            $id = self::create_wc_brands( $term, $site, $creds );
        }
        if ( ! $id ) {
            $id = self::create_wp_rest( $term, $site, $creds );
        }

        if ( $id ) {
            self::cache_id( $site['id'], $term->term_id, $id );
        }

        return $id;
    }

    // -------------------------------------------------------------------------
    // WooCommerce brands endpoint (Perfect Brands / YITH / native WC 9.x+)
    // -------------------------------------------------------------------------

    private static function find_wc_brands( string $slug, array $site, string $creds ): ?int {
        $url = trailingslashit( $site['site_url'] ) . 'wp-json/wc/v3/products/brands';
        $res = wp_remote_get( add_query_arg( [ 'slug' => $slug, 'per_page' => 1 ], $url ), [
            'timeout' => 8,
            'headers' => [ 'Authorization' => 'Basic ' . $creds ],
        ] );
        if ( is_wp_error( $res ) || wp_remote_retrieve_response_code( $res ) !== 200 ) return null;
        $body = json_decode( wp_remote_retrieve_body( $res ), true );
        return ! empty( $body[0]['id'] ) ? (int) $body[0]['id'] : null;
    }

    private static function create_wc_brands( \WP_Term $term, array $site, string $creds ): ?int {
        $url = trailingslashit( $site['site_url'] ) . 'wp-json/wc/v3/products/brands';
        $res = wp_remote_post( $url, [
            'timeout' => 10,
            'headers' => [ 'Authorization' => 'Basic ' . $creds, 'Content-Type' => 'application/json' ],
            'body'    => wp_json_encode( self::term_payload( $term ) ),
        ] );
        if ( is_wp_error( $res ) ) return null;
        $code = wp_remote_retrieve_response_code( $res );
        $body = json_decode( wp_remote_retrieve_body( $res ), true );
        if ( $code === 201 && ! empty( $body['id'] ) ) return (int) $body['id'];
        if ( $code === 400 && ( $body['code'] ?? '' ) === 'term_exists' ) {
            return self::find_wc_brands( $term->slug, $site, $creds );
        }
        return null;
    }

    // -------------------------------------------------------------------------
    // WordPress taxonomy REST API (/wp/v2/product_brand)
    // -------------------------------------------------------------------------

    private static function find_wp_rest( string $slug, array $site, string $creds ): ?int {
        $url = trailingslashit( $site['site_url'] ) . 'wp-json/wp/v2/' . self::TAXONOMY;
        $res = wp_remote_get( add_query_arg( [ 'slug' => $slug, 'per_page' => 1 ], $url ), [
            'timeout' => 8,
            'headers' => [ 'Authorization' => 'Basic ' . $creds ],
        ] );
        if ( is_wp_error( $res ) || wp_remote_retrieve_response_code( $res ) !== 200 ) return null;
        $body = json_decode( wp_remote_retrieve_body( $res ), true );
        return ! empty( $body[0]['id'] ) ? (int) $body[0]['id'] : null;
    }

    private static function create_wp_rest( \WP_Term $term, array $site, string $creds ): ?int {
        $url = trailingslashit( $site['site_url'] ) . 'wp-json/wp/v2/' . self::TAXONOMY;
        $res = wp_remote_post( $url, [
            'timeout' => 10,
            'headers' => [ 'Authorization' => 'Basic ' . $creds, 'Content-Type' => 'application/json' ],
            'body'    => wp_json_encode( self::term_payload( $term ) ),
        ] );
        if ( is_wp_error( $res ) ) return null;
        $code = wp_remote_retrieve_response_code( $res );
        $body = json_decode( wp_remote_retrieve_body( $res ), true );
        if ( $code === 201 && ! empty( $body['id'] ) ) return (int) $body['id'];
        if ( $code === 400 && ( $body['code'] ?? '' ) === 'term_exists' ) {
            return self::find_wp_rest( $term->slug, $site, $creds );
        }
        return null;
    }

    // -------------------------------------------------------------------------
    // Link brands to the product on the destination site
    // -------------------------------------------------------------------------

    /**
     * Links brands via the WooCommerce product PUT with the 'brands' field (WC 9.x+ or plugin).
     * Falls back to the WordPress Posts REST API if that doesn't work.
     * Also stores meta as a final fallback.
     */
    public static function assign( int $remote_product_id, array $remote_ids, int $local_product_id, array $site ): void {
        if ( empty( $remote_ids ) ) return;

        $creds = self::creds( $site );
        $terms = wp_get_object_terms( $local_product_id, self::TAXONOMY );
        $slugs = is_wp_error( $terms ) ? [] : array_map( fn( $t ) => $t->slug, $terms );

        // Method 0: own webshoptigercatalogsync-sync endpoint on the destination site (most reliable — direct wp_set_object_terms)
        // Works if the plugin is also installed on the destination site
        if ( self::assign_via_sc_endpoint( $remote_product_id, $remote_ids, $slugs, $site, $creds ) ) {
            return;
        }

        // Method 1: WooCommerce product PUT with brands array (WC 9.x+ / Perfect Brands / YITH)
        if ( self::assign_wc_brands_field( $remote_product_id, $remote_ids, $site, $creds ) ) {
            return;
        }

        // Method 2: WordPress Posts REST API taxonomy linking
        self::assign_wp_posts_rest( $remote_product_id, $remote_ids, $site, $creds );

        // Method 3: always store meta as extra safety
        self::assign_meta( $remote_product_id, $remote_ids, $local_product_id, $site, $creds );
    }

    // -------------------------------------------------------------------------
    // Method 0: own webshoptigercatalogsync-sync REST endpoint on the destination site
    // -------------------------------------------------------------------------

    private static function assign_via_sc_endpoint( int $remote_product_id, array $remote_ids, array $slugs, array $site, string $creds ): bool {
        $url = trailingslashit( $site['site_url'] ) . 'wp-json/webshoptigercatalogsync-sync/v1/assign-brands';

        $res = wp_remote_post( $url, [
            'timeout' => 15,
            'headers' => [
                'Authorization' => 'Basic ' . $creds,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode( [
                'product_id' => $remote_product_id,
                'term_ids'   => $remote_ids,
                'term_slugs' => $slugs,
            ] ),
        ] );

        if ( is_wp_error( $res ) ) return false;

        $code = wp_remote_retrieve_response_code( $res );
        $body = json_decode( wp_remote_retrieve_body( $res ), true );

        return $code === 200 && ! empty( $body['success'] );
    }

    private static function assign_wc_brands_field( int $remote_product_id, array $remote_ids, array $site, string $creds ): bool {
        $url     = trailingslashit( $site['site_url'] ) . 'wp-json/wc/v3/products/' . $remote_product_id;
        $brands  = array_map( fn( $id ) => [ 'id' => $id ], $remote_ids );

        $res = wp_remote_request( $url, [
            'method'  => 'PUT',
            'timeout' => 15,
            'headers' => [ 'Authorization' => 'Basic ' . $creds, 'Content-Type' => 'application/json' ],
            'body'    => wp_json_encode( [ 'brands' => $brands ] ),
        ] );

        if ( is_wp_error( $res ) || wp_remote_retrieve_response_code( $res ) !== 200 ) return false;

        $body = json_decode( wp_remote_retrieve_body( $res ), true );
        return ! empty( $body['brands'] );
    }

    private static function assign_wp_posts_rest( int $remote_product_id, array $remote_ids, array $site, string $creds ): void {
        // WooCommerce registers products with rest_base='products' but the WP Posts API
        // uses the post type rest_base. Try both variants.
        foreach ( [ 'product', 'products' ] as $base ) {
            $url = trailingslashit( $site['site_url'] ) . 'wp-json/wp/v2/' . $base . '/' . $remote_product_id;
            $res = wp_remote_request( $url, [
                'method'  => 'PUT',
                'timeout' => 10,
                'headers' => [ 'Authorization' => 'Basic ' . $creds, 'Content-Type' => 'application/json' ],
                'body'    => wp_json_encode( [ self::TAXONOMY => $remote_ids ] ),
            ] );
            if ( ! is_wp_error( $res ) && wp_remote_retrieve_response_code( $res ) === 200 ) {
                $body = json_decode( wp_remote_retrieve_body( $res ), true );
                if ( isset( $body[ self::TAXONOMY ] ) ) return;
            }
        }
    }

    private static function assign_meta( int $remote_product_id, array $remote_ids, int $local_product_id, array $site, string $creds ): void {
        $terms = wp_get_object_terms( $local_product_id, self::TAXONOMY );
        $names = is_wp_error( $terms ) ? [] : array_map( fn( $t ) => $t->name, $terms );
        $slugs = is_wp_error( $terms ) ? [] : array_map( fn( $t ) => $t->slug, $terms );

        $url = trailingslashit( $site['site_url'] ) . 'wp-json/wc/v3/products/' . $remote_product_id;
        wp_remote_request( $url, [
            'method'  => 'PUT',
            'timeout' => 10,
            'headers' => [ 'Authorization' => 'Basic ' . $creds, 'Content-Type' => 'application/json' ],
            'body'    => wp_json_encode( [
                'meta_data' => [
                    [ 'key' => '_sc_brand_names',    'value' => implode( ', ', $names ) ],
                    [ 'key' => '_sc_brand_slugs',    'value' => implode( ',', $slugs ) ],
                    [ 'key' => '_sc_brand_ids',      'value' => implode( ',', $remote_ids ) ],
                    [ 'key' => 'yith_product_brand', 'value' => $slugs[0] ?? '' ],
                    [ 'key' => 'perfect_brands',     'value' => $slugs[0] ?? '' ],
                ],
            ] ),
        ] );
    }

    // -------------------------------------------------------------------------
    // Register taxonomy on the destination site (for sites with the plugin)
    // -------------------------------------------------------------------------

    public static function maybe_register_taxonomy(): void {
        if ( taxonomy_exists( self::TAXONOMY ) ) {
            $tax = get_taxonomy( self::TAXONOMY );
            if ( $tax && ! $tax->show_in_rest ) {
                global $wp_taxonomies;
                $wp_taxonomies[ self::TAXONOMY ]->show_in_rest   = true;
                $wp_taxonomies[ self::TAXONOMY ]->rest_base      = self::TAXONOMY;
                $wp_taxonomies[ self::TAXONOMY ]->rest_namespace = 'wp/v2';
            }
            return;
        }

        register_taxonomy( self::TAXONOMY, [ 'product' ], [
            'label'             => 'Brands',
            'labels'            => [
                'name'          => 'Brands',
                'singular_name' => 'Brand',
                'search_items'  => 'Search brands',
                'all_items'     => 'All brands',
                'edit_item'     => 'Edit brand',
                'update_item'   => 'Update brand',
                'add_new_item'  => 'Add new brand',
                'new_item_name' => 'New brand name',
                'menu_name'     => 'Brands',
            ],
            'hierarchical'      => false,
            'show_ui'           => true,
            'show_in_rest'      => true,
            'rest_base'         => self::TAXONOMY,
            'rest_namespace'    => 'wp/v2',
            'show_admin_column' => true,
            'rewrite'           => [ 'slug' => 'brand' ],
            'query_var'         => true,
        ] );
    }

    // -------------------------------------------------------------------------
    // Cache — persistent storage in wp_options (same approach as CategorySync)
    // -------------------------------------------------------------------------

    private static function option_key( int $site_id ): string {
        return 'webshoptigercatalogsync_brand_map_' . $site_id;
    }

    private static function get_cached_id( int $site_id, int $local_term_id ): ?int {
        $map = get_option( self::option_key( $site_id ), [] );
        return isset( $map[ $local_term_id ] ) ? (int) $map[ $local_term_id ] : null;
    }

    private static function cache_id( int $site_id, int $local_term_id, int $remote_id ): void {
        $key = self::option_key( $site_id );
        $map = get_option( $key, [] );
        $map[ $local_term_id ] = $remote_id;
        update_option( $key, $map, false );
    }

    /**
     * Validate the brand cache against the destination site.
     * Fetches all brands in one API call and removes stale IDs.
     * Called at the start of every sync run.
     */
    public static function validate_cache( array $site ): void {
        $map = get_option( self::option_key( $site['id'] ), [] );

        $creds   = self::creds( $site );
        $remote  = [];

        // Try the WC brands endpoint
        $wc_url = trailingslashit( $site['site_url'] ) . 'wp-json/wc/v3/products/brands';
        $res = wp_remote_get( add_query_arg( [ 'per_page' => 100, 'hide_empty' => 'false', '_fields' => 'id,slug' ], $wc_url ), [
            'timeout' => 15,
            'headers' => [ 'Authorization' => 'Basic ' . $creds ],
        ] );
        if ( ! is_wp_error( $res ) && wp_remote_retrieve_response_code( $res ) === 200 ) {
            foreach ( json_decode( wp_remote_retrieve_body( $res ), true ) ?: [] as $b ) {
                $remote[ (int) $b['id'] ] = $b['slug'];
            }
        }

        // Fallback: WP REST taxonomy endpoint
        if ( empty( $remote ) ) {
            $wp_url = trailingslashit( $site['site_url'] ) . 'wp-json/wp/v2/' . self::TAXONOMY;
            $res = wp_remote_get( add_query_arg( [ 'per_page' => 100, 'hide_empty' => 'false', '_fields' => 'id,slug' ], $wp_url ), [
                'timeout' => 15,
                'headers' => [ 'Authorization' => 'Basic ' . $creds ],
            ] );
            if ( ! is_wp_error( $res ) && wp_remote_retrieve_response_code( $res ) === 200 ) {
                foreach ( json_decode( wp_remote_retrieve_body( $res ), true ) ?: [] as $b ) {
                    $remote[ (int) $b['id'] ] = $b['slug'];
                }
            }
        }

        if ( empty( $remote ) ) return;

        // Remove stale IDs from the cache
        $changed = false;
        foreach ( $map as $local_id => $remote_id ) {
            if ( ! isset( $remote[ (int) $remote_id ] ) ) {
                unset( $map[ $local_id ] );
                $changed = true;
            }
        }

        // Also fill in missing brands in the cache based on slug matching
        $local_terms = get_terms( [ 'taxonomy' => self::TAXONOMY, 'hide_empty' => false ] );
        if ( ! is_wp_error( $local_terms ) ) {
            foreach ( $local_terms as $term ) {
                if ( isset( $map[ $term->term_id ] ) ) continue;
                // Look up the remote ID based on slug
                $remote_id = array_search( $term->slug, $remote, true );
                if ( $remote_id !== false ) {
                    $map[ $term->term_id ] = (int) $remote_id;
                    $changed = true;
                }
            }
        }

        if ( $changed ) {
            update_option( self::option_key( $site['id'] ), $map, false );
        }
    }

    // -------------------------------------------------------------------------
    // Clear cache
    // -------------------------------------------------------------------------

    public static function clear_cache( int $site_id ): void {
        delete_option( self::option_key( $site_id ) );
        global $wpdb;
        // phpcs:ignore // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query( $wpdb->prepare(
            "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
            '_transient_sc_brand_' . $site_id . '_%',
            '_transient_timeout_sc_brand_' . $site_id . '_%'
        ) );
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private static function creds( array $site ): string {
        return base64_encode( $site['api_key'] . ':' . $site['api_secret'] );
    }

    private static function term_payload( \WP_Term $term ): array {
        $payload = [ 'name' => $term->name, 'slug' => $term->slug ];
        if ( ! empty( $term->description ) ) {
            $payload['description'] = $term->description;
        }
        $thumbnail_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
        if ( $thumbnail_id ) {
            $img_url = wp_get_attachment_url( (int) $thumbnail_id );
            if ( $img_url ) $payload['image'] = [ 'src' => $img_url ];
        }
        return $payload;
    }
}
