<?php

/**
 * Vendor Product Import/Export Tools
 *
 * This is the controller class for all front end actions
 *
 * @package    WCVendors_Pro\Dashboard\Product
 * @author     Jamie Madden <support@wcvendors.com>
 */
class WCVendors_Pro_Import_Export {

    /**
     * The ID of this plugin.
     *
     * @since    1.0.0
     * @access   private
     * @var      string $wcvendors_pro The ID of this plugin.
     */
    private $wcvendors_pro;

    /**
     * The version of this plugin.
     *
     * @since    1.0.0
     * @access   private
     * @var      string $version The current version of this plugin.
     */
    private $version;

    /**
     * Is the plugin in debug mode
     *
     * @since    1.0.0
     * @access   private
     * @var      bool $debug plugin is in debug mode
     */
    private $debug;

    /**
     * Is the plugin base directory
     *
     * @since    1.0.0
     * @access   private
     * @var      string $base_dir string path for the plugin directory
     */
    private $base_dir;

    /**
     * The includes directory.
     *
     * @var string
     */
    private $includes_dir;

    /**
     * Logger.
     *
     * @var WC_Logger
     */
    private $logger;

    /**
     * Logging enabled.
     *
     * @var bool
     */
    private $logging_enabled;

    /**
     * Initialize the class and set its properties.
     *
     * @since    1.0.0
     *
     * @param string $wcvendors_pro The name of the plugin.
     * @param string $version       The version of this plugin.
     * @param bool   $debug Whether debug is enabled or not.
     */
    public function __construct( $wcvendors_pro, $version, $debug ) {

        $this->wcvendors_pro   = $wcvendors_pro;
        $this->version         = $version;
        $this->debug           = $debug;
        $this->base_dir        = plugin_dir_path( __DIR__ );
        $this->includes_dir    = $this->base_dir . 'public/includes/import-export/';
        $this->logger          = $this->get_logger();
        $this->logging_enabled = wc_string_to_bool( get_option( 'wcvendors_import_export_enable_logging', 'no' ) );
    }

    /**
     * Get logger.
     *
     * @since 1.9.7
     *
     * @return WC_Logger
     */
    public function get_logger() {
        if ( ! $this->logger ) {
            $this->logger = wc_get_logger();
        }

        return $this->logger;
    }

    /**
     * Get logging enabled.
     *
     * @since 1.9.7
     *
     * @return bool
     */
    public function get_logging_enabled() {
        return $this->logging_enabled;
    }

    /**
     * Add the import and export sub pages under products
     *
     * @param array $pages The list of pages.
     */
    public function add_subpages( $pages ) {

        // Import Page.
        if ( wc_string_to_bool( get_option( 'wcvendors_capability_products_import', false ) ) ) {
            // This page doesn't need a template.
            $pages['import'] = array(
                'parent' => 'product',
                'slug'   => 'import',
            );
        }

        // Export page.
        if ( wc_string_to_bool( get_option( 'wcvendors_capability_products_export', false ) ) ) {
            include_once 'includes/import-export/export/class-wcv-product-csv-exporter.php';
            $pages['export'] = array(
                'parent'        => 'product',
                'slug'          => 'export',
                'base_dir'      => $this->base_dir . '/templates/dashboard/product/',
                'template_name' => 'exporter',
                'args'          => array(
                    'exporter' => new WCV_Product_CSV_Exporter(),
                ),
            );
        }

        return $pages;
    }

    /**
     * Add Import custom page to the vendor navigation
     *
     * @since 1.8.0
     *
     * @param object $object The object.
     * @param int    $object_id The object ID.
     * @param string $template The template.
     * @param string $custom The custom.
     * @return void
     * @version 1.0.0
     * @since   1.0.0
     */
    public function add_import_page( $object, $object_id, $template, $custom ) { // phpcs:ignore

        if ( 'product' === $object && 'import' === $custom ) {
            include_once $this->includes_dir . 'import/class-wcv-product-csv-importer.php';
            include_once $this->includes_dir . 'importers/class-wcv-product-csv-importer-controller.php';
            $import_controller = new \WCV_Product_CSV_Importer_Controller();
            $import_controller->dispatch();
        }
    }

    /**
     * Handle the import steps sequence for multi-step form. This comes from the WooCommerce product importer
     *
     * @since 1.8.0
     */
    public function handle_steps() {

        $posted_data = wp_unslash( $_POST ); // phpcs:ignore

        include_once $this->includes_dir . 'importers/class-wcv-product-csv-importer-controller.php';
        $import_controller = new \WCV_Product_CSV_Importer_Controller();

        if ( ! empty( $posted_data['save_step'] ) && ! empty( $import_controller->steps[ $import_controller->step ]['handler'] ) ) {
            call_user_func( $import_controller->steps[ $import_controller->step ]['handler'], $import_controller );
        }
    }

    /**
     * Disable the featured product column so that vendors can't import products and set them as featured.
     *
     * @param array $data the row data to filter.
     * @return array $data the filtered row data.
     */
    public function disable_featured_column( $data ) {

        if ( WCV_Vendors::is_vendor( get_current_user_id() ) ) {
            $data['featured'] = false;
        }

        return $data;
    }

    /**
     * Check the product status based on the marketplace settings
     *
     * @param WC_Product $object The product object.
     * @param array      $data Raw CSV data.
     *
     * @version 1.9.6 - Fix pending import in admin.
     * @since   1.9.8 - Fix the -1 published status is not being set to draft.
     */
    public function check_product_status( $object, $data ) { // phpcs:ignore

        $author = get_current_user_id();

        if ( ! WCV_Vendors::is_vendor( $author ) ) {
            return $object;
        }

        $status = isset( $data['status'] ) ? $data['status'] : 'draft';

        $skipped_statuses = apply_filters( 'wcvendors_product_import_skipped_check_product_status', array( 'draft', 'private' ) );

        if ( in_array( $status, $skipped_statuses, true ) ) {
            $object->set_status( $status );
            return $object;
        }

        // Get publishing permission for marketplace and vendor override.
        $can_submit_live  = wc_string_to_bool( get_option( 'wcvendors_capability_products_live', 'no' ) );
        $trusted_vendor   = wc_string_to_bool( get_user_meta( $author, '_wcv_trusted_vendor', true ) );
        $untrusted_vendor = wc_string_to_bool( get_user_meta( $author, '_wcv_untrusted_vendor', true ) );

        if ( $object->is_type( 'variation' ) ) {
            $object->set_status( 'publish' );
            return $object;
        }

        // If they can't submit live products, make the status pending.
        if ( $can_submit_live ) {
            $object->set_status( 'publish' );
        } else {
            $object->set_status( 'pending' );
        }

        // If the vendor is untrusted make the status pending.
        if ( $untrusted_vendor ) {
            $object->set_status( 'pending' );
        }

        // If the vendor is trusted they can publish straight away no matter the setting.
        if ( $trusted_vendor ) {
            $object->set_status( 'publish' );
        }

        return $object;
    }

    /**
     * Check product owner to ensure that products can only be updated / inserted if owned by the vendor importing
     *
     * @param array $data import row to check.
     */
    public function check_product_owner( $data ) {

        $vendor_id = get_current_user_id();

        // Initialize import_product_id and safely check for id or sku.
        $import_product_id = 0;

        // Check if id exists in data.
        if ( isset( $data['id'] ) && ! empty( $data['id'] ) ) {
            $import_product_id = $data['id'];
        } elseif ( isset( $data['sku'] ) && ! empty( $data['sku'] ) ) { // If no id, check for sku.
            $product_id = wc_get_product_id_by_sku( $data['sku'] );
            if ( $product_id ) {
                $import_product_id = $product_id;
            }
        }

        if ( empty( $import_product_id ) || 0 === (int) $import_product_id ) {
            return $data;
        }

        // Get the vendor id of the product.
        $product_vendor_id = absint( get_post_field( 'post_author', $import_product_id ) );

        // remove product_id and sku to create a new product if the vendor doesn't own the product.
        if ( (int) $product_vendor_id !== (int) $vendor_id ) {
            $data['sku'] = 0;
            if ( isset( $data['id'] ) ) {
                $data['id'] = 0;
            }
        }

        return $data;
    }

    /**
     * Clean orphaned records in the product meta lookup table.
     *
     * This function removes records from wp_wc_product_meta_lookup table
     * where the corresponding product no longer exists in wp_posts table.
     * This prevents SKU lookup conflicts during import.
     *
     * @return int Number of orphaned records cleaned
     *
     * @since 1.9.7
     * @version 1.9.7
     */
    public function clean_orphaned_product_meta_lookup() {
        $deleted_count = self::clean_orphaned_product_meta_lookup_static();

        if ( $deleted_count > 0 && $this->debug ) {
            WCVendors_Pro::log(
                sprintf(
                    'WC Vendors Pro: Cleaned %d orphaned records from product meta lookup table',
                    $deleted_count
                )
            );
        }

        return $deleted_count;
    }

    /**
     * Static method to clean orphaned records in the product meta lookup table.
     *
     * This function removes records from wp_wc_product_meta_lookup table
     * where the corresponding product no longer exists in wp_posts table.
     * This prevents SKU lookup conflicts during import.
     *
     * @return int Number of orphaned records cleaned
     *
     * @since 1.9.7
     * @version 1.9.7
     */
    public static function clean_orphaned_product_meta_lookup_static() {
        global $wpdb;

        // Get the product meta lookup table name.
        $product_meta_lookup_table = $wpdb->prefix . 'wc_product_meta_lookup';

        // Check if the table exists.
        $table_exists = $wpdb->get_var(
            $wpdb->prepare(
                'SHOW TABLES LIKE %s',
                $product_meta_lookup_table
            )
        );

        if ( ! $table_exists ) {
            return 0;
        }

        // Delete orphaned records from product meta lookup table.
        $deleted_count = $wpdb->query(
            "DELETE pml FROM {$wpdb->prefix}wc_product_meta_lookup pml
            LEFT JOIN {$wpdb->posts} p ON p.ID = pml.product_id
            WHERE p.ID IS NULL"
        );

        return $deleted_count;
    }

    /**
     * Ajax callback for importing one batch of products from a CSV.
     *
     * @since 1.9.7
     * @version 1.9.7 - Add cleanup of orphaned product meta lookup records, add logging.
     */
    public function do_ajax_product_import() {
        global $wpdb;

        $post_data = wp_unslash( $_POST ); // phpcs:ignore

        if ( ! isset( $post_data['file'] ) ) {
            wp_die( -1 );
        }

        include_once $this->includes_dir . 'importers/class-wcv-product-csv-importer-controller.php';
        include_once $this->includes_dir . 'import/class-wcv-product-csv-importer.php';

        // phpcs:disable
        $file   = wc_clean( wp_unslash( $_POST['file'] ) );
        $params = array(
            'delimiter'       => ! empty( $_POST['delimiter'] ) ? wc_clean( wp_unslash( $_POST['delimiter'] ) ) : ',',
            'start_pos'       => isset( $_POST['position'] ) ? absint( $_POST['position'] ) : 0,
            'mapping'         => isset( $_POST['mapping'] ) ? (array) wc_clean( wp_unslash( $_POST['mapping'] ) ) : array(),
            'update_existing' => isset( $_POST['update_existing'] ) ? (bool) $_POST['update_existing'] : false,
            'hybrid_mode'     => isset( $_POST['hybrid_mode'] ) ? (bool) $_POST['hybrid_mode'] : false,
            'lines'           => apply_filters( 'woocommerce_product_import_batch_size', 30 ),
            'parse'           => true,
        );
        // phpcs:enable

        // Clean orphaned records in product meta lookup table before import starts.
        // This prevents SKU lookup conflicts during import.
        if ( 0 === (int) $params['start_pos'] ) {
            if ( $this->get_logging_enabled() ) {
                $this->logger->log( 'info', 'Starting import from file: ' . basename( $file ), array( 'source' => 'wcvendors-pro-product-importer-vendor-' . get_current_user_id() ) );
                $this->logger->log( 'info', 'Cleaning orphaned product meta lookup records before import starts.', array( 'source' => 'wcvendors-pro-product-importer-vendor-' . get_current_user_id() ) );
            }
            $this->clean_orphaned_product_meta_lookup();

            if ( $this->get_logging_enabled() ) {
                $this->logger->log( 'info', 'Orphaned product meta lookup records cleaned.', array( 'source' => 'wcvendors-pro-product-importer-vendor-' . get_current_user_id() ) );
            }
        }

        // Log failures.
        if ( 0 !== $params['start_pos'] ) {
            $error_log = array_filter( (array) get_user_option( 'product_import_error_log' ) );
        } else {
            $error_log = array();
        }

        $importer = WCV_Product_CSV_Importer_Controller::get_importer( $file, $params );
        $importer->set_logger( $this->get_logger() );
        $importer->set_logging_enabled( $this->get_logging_enabled() );

        $results          = $importer->import();
        $percent_complete = $importer->get_percent_complete();
        $error_log        = array_merge( $error_log, $results['failed'], $results['skipped'] );

        update_user_option( get_current_user_id(), 'product_import_error_log', $error_log );

        if ( 100 === $percent_complete ) {
            // @codingStandardsIgnoreStart.
            $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_original_id' ) );
            $wpdb->delete( $wpdb->posts, array(
                'post_type'   => 'product',
                'post_status' => 'importing',
            ) );
            $wpdb->delete( $wpdb->posts, array(
                'post_type'   => 'product_variation',
                'post_status' => 'importing',
            ) );
            // @codingStandardsIgnoreEnd.

            // Clean up orphaned data.
            $wpdb->query(
                "
                DELETE {$wpdb->posts}.* FROM {$wpdb->posts}
                LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->posts}.post_parent
                WHERE wp.ID IS NULL AND {$wpdb->posts}.post_type = 'product_variation'
            "
            );
            $wpdb->query(
                "
                DELETE {$wpdb->postmeta}.* FROM {$wpdb->postmeta}
                LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->postmeta}.post_id
                WHERE wp.ID IS NULL
            "
            );
            // @codingStandardsIgnoreStart.
            $wpdb->query( "
                DELETE tr.* FROM {$wpdb->term_relationships} tr
                LEFT JOIN {$wpdb->posts} wp ON wp.ID = tr.object_id
                LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                WHERE wp.ID IS NULL
                AND tt.taxonomy IN ( '" . implode( "','", array_map( 'esc_sql', get_object_taxonomies( 'product' ) ) ) . "' )
            " );
            // @codingStandardsIgnoreEnd.

            $url_redirect = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ) ), WCVendors_Pro_Dashboard::get_dashboard_page_url() . 'product/import/?step=done' );
            if ( substr( $_SERVER['HTTP_REFERER'], 0, strlen( get_admin_url() ) ) === get_admin_url() ) {
                $url_redirect = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ) ), admin_url( 'edit.php?post_type=product&page=product_importer&step=done' ) );
            }

            // Send success.
            wp_send_json_success(
                array(
                    'position'            => 'done',
                    'percentage'          => 100,
                    'url'                 => $url_redirect,
                    'imported'            => count( $results['imported'] ),
                    'failed'              => count( $results['failed'] ),
                    'updated'             => count( $results['updated'] ),
                    'skipped'             => count( $results['skipped'] ),
                    'imported_variations' => count( $results['imported_variations'] ),
                )
            );
        } else {
            wp_send_json_success(
                array(
                    'position'            => $importer->get_file_position(),
                    'percentage'          => $percent_complete,
                    'imported'            => count( $results['imported'] ),
                    'failed'              => count( $results['failed'] ),
                    'updated'             => count( $results['updated'] ),
                    'skipped'             => count( $results['skipped'] ),
                    'imported_variations' => count( $results['imported_variations'] ),
                )
            );
        }
    }

    /**
     * Serve the generated file.
     */
    public function download_export_file() {
        if ( isset( $_GET['action'], $_GET['nonce'] ) && wp_verify_nonce( wp_unslash( $_GET['nonce'] ), 'product-csv' ) && 'download_product_csv' === wp_unslash( $_GET['action'] ) ) { // phpcs:ignore
            include_once 'includes/import-export/export/class-wcv-product-csv-exporter.php';
            $exporter = new WCV_Product_CSV_Exporter();

            if ( ! empty( $_GET['filename'] ) ) { // phpcs:ignore
                $exporter->set_filename( wp_unslash( $_GET['filename'] ) ); // phpcs:ignore
            }

            $exporter->export();
        }
    }

    /**
     * AJAX callback for doing the actual export to the CSV file.
     */
    public function do_ajax_product_export() {
        check_ajax_referer( 'wcv-product-export', 'security' );

        include_once 'includes/import-export/export/class-wcv-product-csv-exporter.php';

        $step     = isset( $_POST['step'] ) ? absint( $_POST['step'] ) : 1;
        $exporter = new WCV_Product_CSV_Exporter();

        $exporter->set_logger( $this->get_logger() );
        $exporter->set_logging_enabled( $this->get_logging_enabled() );

        if ( ! empty( $_POST['columns'] ) ) {
            $exporter->set_column_names( wp_unslash( $_POST['columns'] ) );
        }

        if ( ! empty( $_POST['selected_columns'] ) ) {
            $exporter->set_columns_to_export( wp_unslash( $_POST['selected_columns'] ) );
        }

        if ( ! empty( $_POST['export_meta'] ) ) {
            $exporter->enable_meta_export( true );
        }

        if ( ! empty( $_POST['export_types'] ) ) {
            $exporter->set_product_types_to_export( wp_unslash( $_POST['export_types'] ) );
        }

        if ( ! empty( $_POST['export_category'] ) && is_array( $_POST['export_category'] ) ) {
            $exporter->set_product_category_to_export( wp_unslash( array_values( $_POST['export_category'] ) ) );
        }

        if ( ! empty( $_POST['filename'] ) ) {
            $exporter->set_filename( wp_unslash( $_POST['filename'] ) );
        }

        $exporter->set_page( $step );
        $exporter->generate_file();

        $query_args = apply_filters(
            'wcvendors_export_get_ajax_query_args',
            array(
                'nonce'    => wp_create_nonce( 'product-csv' ),
                'action'   => 'download_product_csv',
                'filename' => $exporter->get_filename(),
            )
        );

        if ( 100 === $exporter->get_percent_complete() ) {
            wp_send_json_success(
                array(
                    'step'       => 'done',
                    'percentage' => 100,
                    'url'        => add_query_arg( $query_args, WCVendors_Pro_Dashboard::get_dashboard_page_url( 'product/export/' ) ),
                )
            );
        } else {
            wp_send_json_success(
                array(
                    'step'       => ++$step,
                    'percentage' => $exporter->get_percent_complete(),
                    'columns'    => $exporter->get_column_names(),
                )
            );
        }
    }

    /**
     * Handle manual cleanup of orphaned product meta lookup records.
     *
     * This method is called via WooCommerce debug tools and provides a way
     * to manually trigger the cleanup process.
     *
     * @since 1.9.7
     * @version 1.9.7
     */
    public function handle_manual_cleanup() {
        // Check user capabilities.
        if ( ! current_user_can( 'manage_woocommerce' ) ) {
            if ( $this->debug ) {
                WCVendors_Pro::log( 'User does not have permission to perform this action.' );
            }
            return;
        }

        try {
            // Perform the cleanup.
            $cleaned_count = self::clean_orphaned_product_meta_lookup_static();

            if ( $cleaned_count > 0 ) {
                if ( $this->debug ) {
                    WCVendors_Pro::log( sprintf( 'Successfully cleaned %d orphaned records from the product meta lookup table.', $cleaned_count ) );
                }
            } elseif ( $this->debug ) {
                    WCVendors_Pro::log( 'No orphaned records found to clean. Your product meta lookup table is already clean.' );
            }
        } catch ( Exception $e ) {
            if ( $this->debug ) {
                WCVendors_Pro::log( sprintf( 'Error during cleanup: %s', $e->getMessage() ) );
            }
        }
    }

    /**
     * Add WooCommerce tool for cleaning orphaned product meta lookup records.
     *
     * @param array $tools The list of WooCommerce debug tools.
     * @return array The modified list of WooCommerce debug tools.
     * @since 1.9.7
     * @version 1.9.7
     */
    public function add_cleanup_tool( $tools ) {
        $tools['wcvendors_pro_clean_orphaned_product_meta_lookup'] = array(
            'name'     => __( 'Clean Orphaned Product Meta Lookup', 'wcvendors-pro' ),
            'button'   => __( 'Clean Orphaned Records', 'wcvendors-pro' ),
            'desc'     => __( 'This tool removes orphaned records from the WooCommerce product meta lookup table. Orphaned records occur when products are deleted but their lookup table entries remain, which can cause SKU lookup conflicts during import.', 'wcvendors-pro' ),
            'callback' => array( $this, 'handle_manual_cleanup' ),
        );
        return $tools;
    }
}
