<?php
/**
 * Class Import\Wordpress
 *
 * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 * @contributor Uldis Zvirbulis <uldis.zvirbulis@mtcmedia.co.uk>
 * @version 04.09.2017
 */
namespace Mtc\Core\Import;

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Mtc\Modules\Members\Models\Member;
use Mtc\Modules\Members\Models\MembersAddress;
use Mtc\Plugins\Refunds\Classes\Refund;
use Mtc\Shop\Assessment\Form;
use Mtc\Shop\Assessment\Question;
use Mtc\Shop\Category;
use Mtc\Shop\Item;
use Mtc\Shop\Order;
use Mtc\Shop\Warning;

/**
 * Class Import\Wordpress
 *
 * WordPress import class.
 * Allows importing information for product from a remote WordPress database
 *
 * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 * @contributor Uldis Zvirbulis <uldis.zvirbulis@mtcmedia.co.uk>
 * @version 04.09.2017
 */
class Wordpress extends BaseImport
{

    private static $status_map = [
        'wc-approved'           => 3,
        'wc-completed'          => 4,
        'wc-cancelled'          => 9,
        'wc-refunded'           => 6,
        'wc-pend-e6ad234360'    => 0,
        'wc-processing'         => 3,
        'wc-on-hold'            => 1,
    ];

    private static $question_type_map = [
        'select' => 'variations',
        'radio' => 'variations',
    ];

    /**
     * Import Member information from WordPress
     *
     * Uses wp_users & wp_usermeta tables to gather information.
     * Will delete existing member records if Truncate is true
     */
    public function importMembers()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Members \n";
            Member::truncate();
            MembersAddress::truncate();
        }

        echo "Importing members... \n";
        // Fetch All member entries from DB
        DB::connection('import')
            ->table('wp_users')
            ->get()
            ->each(function ($member_record) {
                /*
                 * Check if member already exists in database.
                 * This might happen if we skipped truncate and have already run import previously
                 * We don't need to create member record if it already exists.
                 * Member data update is not done.
                 */
                if (!$this->truncate && Member::find($member_record->ID)) {
                    return;
                }

                // Fetch user metadata
                $user_meta = DB::connection('import')->table('wp_usermeta')
                    ->where('user_id', $member_record->ID)
                    ->get()
                    ->keyBy('meta_key')
                    ->map(function ($meta) {
                        return $meta->meta_value;
                    })->toArray();

                /*
                 * Create a member record.
                 * We need to make sure correct member ID is passed through.
                 * WordPress Passwords are encrypted so we cannot migrate them over so we are leaving them empty.
                 */
                $member = new Member();
                $member->id = $member_record->ID;
                $member->fill([
                    'email' => $member_record->user_email,
                    'contact_no' => $user_meta['billing_phone'] ?? ''
                ]);
                $member->save();

                // Create users billing address
                $member->addresses()->create([
                    'type' => 'billing',
                    'firstname' => $user_meta['billing_first_name'] ?? '',
                    'lastname' => $user_meta['billing_last_name'] ?? '',
                    'address1' => $user_meta['billing_address_1'] ?? '',
                    'address2' => $user_meta['billing_address_2'] ?? '',
                    'city' => $user_meta['billing_city'] ?? '',
                    'state' => $user_meta['billing_state'] ?? '',
                    'country' => $user_meta['billing_country'] ?? '',
                    'postcode' => $user_meta['billing_postcode'] ?? ''
                ]);

                if (!empty($user_meta['shipping_country']) && !empty($user_meta['shipping_first_name'])) {
                    // Create users shipping address
                    $member->addresses()->create([
                        'type' => 'shipping',
                        'firstname' => $user_meta['shipping_first_name'] ?? '',
                        'lastname' => $user_meta['shipping_last_name'] ?? '',
                        'address1' => $user_meta['shipping_address_1'] ?? '',
                        'address2' => $user_meta['shipping_address_2'] ?? '',
                        'city' => $user_meta['shipping_city'] ?? '',
                        'state' => $user_meta['shipping_state'] ?? '',
                        'country' => $user_meta['shipping_country'] ?? '',
                        'postcode' => $user_meta['shipping_postcode'] ?? ''
                    ]);
                }
            });
        echo "Members Imported \n";
    }

    /**
     * Import Categories from WordPress
     *
     */
    public function importCategories()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Categories \n";
            DB::table('categories')->truncate();
        }

        $schema = DB::connection()->getSchemaBuilder();

        if (!$schema->hasColumn('categories', 'old_slug')) {
            $schema->table('categories', function($table) {
                $table->string('old_slug')->after('name');
            });
        }

        echo "Importing categories... \n";
        DB::connection('import')
            ->table('wp_term_taxonomy')
            ->where('taxonomy', 'product_cat')
            ->join('wp_terms', 'wp_terms.term_id', '=', 'wp_term_taxonomy.term_id')
            ->get()
            ->each(function ($row) {
                $category = new Category();
                if ($this->truncate) {
                    $category->id = $row->term_id;
                } else {
                    $category->firstOrNew([
                        'id' => $row->term_id
                    ]);
                }

                $category->fill([
                    'sub_id' => $row->parent,
                    'name' => $row->name,
                    'old_slug' => $row->slug,
                    'slug' => $row->slug,
                    'description' => $row->description,
                ]);

                // Import online doctor category data
                // If there's a category whose parent is 'Online Doctor' category and the Slug match
                if ($online_doctor_category = DB::connection('import')
                    ->table('wp_posts')
                    ->where('post_parent', '3896')
                    ->where('post_name', $row->slug)
                    ->first()
                ) {
                    $category_meta = self::getPostMeta($online_doctor_category->ID);
                    $category->is_online_doctor = 1;
                    $category->overview = $category_meta['tab_overview'] ?? '';
                    $category->causes = $category_meta['tab_causes'] ?? '';
                    $category->symptoms = $category_meta['tab_symptoms'] ?? '';
                    $category->treatments = $category_meta['tab_treatments'] ?? '';
                    $category->faq = $category_meta['tab_faq'] ?? '';
                    $category->cautions = $category_meta['tab_cautions'] ?? '';
                    $category->side_effects = $category_meta['tab_side_effects'] ?? '';
                    $category->information_leaflet = $category_meta['tab_information_leaflet'] ?? '';

                    $subheading = !empty($category_meta['sub-heading']) ?
                        '<h2>' . $category_meta['sub-heading'] . '</h2>' :
                        '';

                    $description = strip_tags($online_doctor_category->post_content, '<p><a><ul><li><h2><h3>');
                    $description = preg_replace('/(<[^>]+) style=".*?"/i', '$1', $description);
                    $category->description = $subheading . $description;

                    if (
                        !empty($category_meta['_thumbnail_id']) &&
                        $old_image = DB::connection('import')
                            ->table('wp_posts')
                            ->where('ID', $category_meta['_thumbnail_id'])
                            ->first()
                    ) {
                        $image_meta = self::getPostMeta($old_image->ID);

                        $image_meta['_wp_attached_file'];
                        $new_name = end(explode('/',  $image_meta['_wp_attached_file']));
                        if (self::remoteURLExists('https://www.cloudpharmacy.co.uk/wp-content/uploads/' . $image_meta['_wp_attached_file'])) {
                            foreach ($this->image_folders['category_images'] as $folder_name => $resize_params) {
                                $this->saveRemoteImage('wp-content/uploads/' . $image_meta['_wp_attached_file'], $new_name, $resize_params);
                            }
                            $category->image = $new_name ?? '';
                        }
                    }
                }

                $category->save();
            });
        echo "Categories Imported \n";
    }

    /**
     * Import Products from WordPress
     *
     */
    public function importProducts()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Items Sizes Custom \n";
            DB::table('items_sizes_custom')->truncate();
            echo "Truncating Items Sizes \n";
            DB::table('items_sizes')->truncate();
            echo "Truncating Items Custom\n";
            DB::table('items_custom')->truncate();
            echo "Truncating Items Images\n";
            DB::table('items_images')->truncate();
            echo "Truncating Items \n";
            DB::table('items')->truncate();
            echo "Truncating Warnings \n";
            DB::table('warning_items')->truncate();
            DB::table('warnings')->truncate();
        }

        $schema = DB::connection()->getSchemaBuilder();

        echo "Importing products... \n";
        // Fetch All product entries from DB
        DB::connection('import')
            ->table('wp_posts')
            ->where('post_type', 'product')
            ->get()
            ->each(function ($old_product) {

                // Fetch product metadata
                $product_meta = self::getPostMeta($old_product->ID);

                $product = new Item();
                // If truncated, no need to call DB
                if ($this->truncate) {
                    $product->id = $old_product->ID;
                } else {
                    $product->findOrNew([
                        'id' => $old_product->ID
                    ]);
                }

                if (!empty($product_meta['_price'])) {
                    if ($product_meta['_regular_price'] > 0) {
                        $price = (float)$product_meta['_regular_price'];
                    } else {
                        $price = (float)$product_meta['_price'];
                    }
                    $sale_price = (float)$product_meta['_sale_price'];
                } else {
                    if ($product_meta['_regular_price'] !== '') {
                        $price = (float)$product_meta['_regular_price'];
                        $sale_price = (float)$product_meta['_sale_price'];
                    } else {
                        $price = (float)$product_meta['_min_variation_regular_price'];
                        $sale_price = (float)$product_meta['_min_variation_sale_price'];
                    }
                }

                $stock = $product_meta['_stock'] ?? '';

                $product_description = $old_product->post_content;

                $custom_description_fields = [
                    'description' => '',
                    'directions' => '',
                    'warnings' => '',
                    'ingredients' => '',
                ];

                foreach ($custom_description_fields as $key => $content) {
                    $start = '<div id="' . $key . '">';
                    $end = '</div>';
                    $delimiter = '#';
                    $regex = $delimiter . preg_quote($start, $delimiter)
                        . '(.*?)'
                        . preg_quote($end, $delimiter)
                        . $delimiter
                        . 's';
                    preg_match($regex, $product_description, $matches);
                    if (!empty($matches)) {
                        $custom_description_fields[$key] = $matches[1];
                    }
                }


                $product->fill([
                    'name' => $old_product->post_title,
                    'description' => $custom_description_fields['description'],
                    'price' => (float)$price,
                    'price_exvat' => (float)$price,
                    'sale_price' => (float)$sale_price,
                    'sale_price_exvat' => (float)$sale_price,
                    'sort_price' => (float)($sale_price > 0 ?: $price),
                    'hidden' => $old_product->post_status === 'publish' ? 0 : 1,
                    'stock' => $stock !== '' ?: ($product_meta['_stock_status'] === 'instock' ? 99999999 : 0),
                    'vat_rate' => 20,
                    'weight' => $product_meta['_weight'],
                    'vat_deductable' => 0,
                ]);
                $product->created_at = $old_product->post_date;
                $product->save();

                if (!empty($product_meta['product_warnings'])) {
                    $warning_ids = Warning::importWarnings($product_meta['product_warnings']);
                    $product->warnings()->syncWithoutDetaching($warning_ids);
                }

                // Item custom
                $item_custom = new Item\Custom();
                $item_custom->findOrNew([
                    'item_id' => $old_product->ID,
                ]);

                $item_custom->fill([
                    'item_id' => $old_product->ID,
                    'slug' => $old_product->post_name,
                    'cog_cost' => $product_meta['_wc_cog_cost'] ?? '',
                    'vat_code' => 'S',
                    'indication' => $custom_description_fields['directions'],
                    'caution' => $custom_description_fields['warnings'],
                    'contents' => $custom_description_fields['ingredients'],
                ]);

                $item_custom->save();

                // Item sizes
                DB::connection('import')
                    ->table('wp_posts')
                    ->where('post_type', 'product_variation')
                    ->where('post_parent', $old_product->ID)
                    ->get()
                    ->each(function ($old_size) {
                        // Fetch size metadata
                        $size_meta = self::getPostMeta($old_size->ID);

                        $size = new Item\Size();
                        // If truncated, no need to call DB
                        if ($this->truncate) {
                            $size->id = $old_size->ID;
                        } else {
                            $size->findOrNew([
                                'id' => $old_size->ID
                            ]);
                        }

                        $quantity = $size_meta['attribute_quantity'] ?? $size_meta['attribute_pa_quantity'] ?? '';
                        $strength = $size_meta['attribute_strength'] ?? $size_meta['attribute_pa_strength'] ?? '';
                        $size_value = trim($quantity . ' ' . $strength);

                        $stock = $size_meta['_stock'] ?? '';

                        $size->fill([
                            'item_id' => $old_size->post_parent,
                            'size' => $size_value,
                            'quantity' => $quantity ?? '',
                            'strength' => $strength ?? '',
                            'price' => (float)$size_meta['_regular_price'],
                            'price_exvat' => (float)$size_meta['_regular_price'],
                            'sale_price' => (float)($size_meta['_price'] < $size_meta['_regular_price'] ? $size_meta['_price'] : 0),
                            'sale_price_exvat' => (float)($size_meta['_price'] < $size_meta['_regular_price'] ? $size_meta['_price'] : 0),
                            'stock' => $stock !== '' ?: ($size_meta['_stock_status'] === 'instock' ? 99999999 : 0),
                            'hide' => $old_size->post_status === 'publish' ? 0 : 1,
                        ]);
                        $size->save();

                        // Item sizes custom

                        $size_custom = [
                            'item_id' => $old_size->post_parent,
                            'size_id' => $old_size->ID,
                            'cog_cost' => $size_meta['_wc_cog_cost'] ?? '',
                        ];

                        if (DB::table('items_sizes_custom')
                            ->where('item_id', $old_size->post_parent)
                            ->where('size_id', $old_size->ID)
                            ->count())
                        {
                            DB::table('items_sizes_custom')
                                ->where('item_id', $old_size->post_parent)
                                ->where('size_id', $old_size->ID)
                                ->update($size_custom);
                        } else {

                            DB::table('items_sizes_custom')
                                ->insert($size_custom);
                        }
                    });

                // Item images
                if (
                    !empty($product_meta['_thumbnail_id']) &&
                    $old_image = DB::connection('import')
                        ->table('wp_posts')
                        ->where('ID', $product_meta['_thumbnail_id'])
                        ->first()
                ) {
                    $image_meta = self::getPostMeta($old_image->ID);

                    $image = new Item\Image();

                    $image->findOrNew([
                        'item_id' => $old_product->ID
                    ]);

                    $image->fill([
                        'item_id' => $old_product->ID,
                        'name' => end(explode('/',  $image_meta['_wp_attached_file'])),
                        'old_location' => '/wp-content/uploads/' . $image_meta['_wp_attached_file'],
                        'default' => '1',
                    ]);
                    $image->save();
                }

            });

        echo "Products Imported \n";

        $this->importProductCategories();
    }

    /**
     * Import Product categories from WordPress
     *
     */
    public function importProductCategories()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            //echo "Truncating Items Categories \n";
            //DB::table('items_categories')->truncate();
        }

        echo "Importing product categories... \n";
        DB::connection('import')
            ->table('wp_term_relationships')
            ->join('wp_term_taxonomy', 'wp_term_taxonomy.term_taxonomy_id', '=', 'wp_term_relationships.term_taxonomy_id')
            ->where('taxonomy', 'product_cat')
            ->orderBy('parent')
            ->get()
            ->each(function ($row) {
                // If wasn't truncated and this record already exists, just skip
                if (
                    !$this->truncate &&
                    DB::table('items_categories')
                        ->where('cat_id', $row->term_id)
                        ->where('item_id', $row->object_id)
                        ->count()
                ) {
                    return;
                }
                /*DB::table('items_categories')
                    ->insert([
                        'cat_id' => $row->term_id,
                        'item_id' => $row->object_id,
                    ]);*/
                // Pharmacy category
                if ((int)$row->term_id === 2967) {
                    if ($item = (new Item())->find($row->object_id)) {
                        $item->product_type = 'pharmacy';
                        $item->save();
                    }
                    // Doctor category
                } elseif ((int)$row->term_id === 3424) {
                    if ($item = (new Item())->find($row->object_id)) {
                        // Only save as doctor type if it doesn't have pharmacy category
                        if (!$item->categories()->find(2967)) {
                            $item->vat_rate = 0;
                            $item_custom = $item->custom;
                            $item_custom->vat_code = 'Z';
                            $item_custom->save();
                            $item->product_type = 'doctor';
                            $item->save();
                        }
                    }
                }
                return true;
            });
        echo "Product Categories Imported \n";
    }

    /**
     * Import Orders from WordPress
     *
     */
    public function importOrders()
    {

        $schema = DB::connection()->getSchemaBuilder();

        // Create tables required
        if (!$schema->hasTable('order_items_meta')) {
            // Create table
            $schema->create('order_items_meta', function (Blueprint $table) {
                $table->engine = 'InnoDB';
                $table->increments('id');
                $table->integer('order_id');
                $table->integer('item_id');
                $table->integer('order_item_id');
                $table->string('meta_key');
                $table->longText('meta_value');
                $table->timestamps();
            });
        }

        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Orders \n";
            DB::table('order')->truncate();
            echo "Truncating Order address \n";
            DB::table('order_address')->truncate();
            echo "Truncating Order items \n";
            DB::table('order_items')->truncate();
            echo "Truncating Order info \n";
            DB::table('order_info')->truncate();
            echo "Truncating Order note \n";
            DB::table('order_note')->truncate();
            echo "Truncating Order items meta \n";
            DB::table('order_items_meta')->truncate();
            echo "Truncating Order payments \n";
            DB::table('order_evopay')->truncate();
        }

        echo "Importing orders... \n";
        // Fetch All order entries from DB
        DB::connection('import')
            ->table('wp_posts')
            ->where('post_type', 'shop_order')
            ->whereIn('post_status', array_keys(self::$status_map))
            ->where('post_status', '!=', 'wc-cancelled')
            ->where('post_date', '>=', '2018-02-01 00:00:00')
            ->get()
            ->each(function ($old_order) {
                // Fetch order metadata
                $order_meta = self::getPostMeta($old_order->ID);


                // Order data
                $order = new Order();
                // If truncated, no need to call DB
                if (Order::query()->find($old_order->ID)) {
                    return;
                }
                $order->id = $old_order->ID;

                $delivery = DB::connection('import')
                    ->table('wp_woocommerce_order_items')
                    ->where('order_id', $old_order->ID)
                    ->where('order_item_type', 'shipping')
                    ->first();
                $delivery_meta = DB::connection('import')
                    ->table('wp_woocommerce_order_itemmeta')
                    ->where('order_item_id', $delivery->order_item_id)
                    ->get()
                    ->keyBy('meta_key')
                    ->map(function ($meta) {
                        return $meta->meta_value;
                    })->toArray();

                $status = self::getStatus($old_order->post_status);

                $order->fill([
                    'paid' => 1,
                    'date' => $old_order->post_date,
                    'delivery_name' => $delivery->order_item_name ?? '',
                    'delivery_cost' => $delivery_meta['cost'] ?? 0,
                    'cost_total' => $delivery_meta['cost'] ?? 0,
                    'cost_total_exvat' => $delivery_meta['cost'] ?? 0,
                    'status' => $status,
                    'member' => $order_meta['_customer_user'],
                    'order_ref' => $old_order->ID,
                ]);
                $order->save();

                // Order payment

                if (!empty($order_meta['_paid_date'])) {
                    DB::table('order_evopay')->insert([
                        'order_id' => $old_order->ID,
                        'amount_paid' => $order_meta['_order_total'],
                        'tnx_id' => $old_order->ID,
                        'paid_time' => $order_meta['_paid_date'],
                    ]);
                }

                // Order address
                $order->address()->delete();

                $order->address()->create([
                    'type' => 'billing',
                    'firstname' => $order_meta['_billing_first_name'] ?? '',
                    'lastname' => $order_meta['_billing_last_name'] ?? '',
                    'address1' => $order_meta['_billing_address_1'] ?? '',
                    'address2' => $order_meta['_billing_address_2'] ?? '',
                    'city' => $order_meta['_billing_city'] ?? '',
                    'state' => $order_meta['_billing_state'] ?? '',
                    'country' => $order_meta['_billing_country'] ?? '',
                    'postcode' => $order_meta['_billing_postcode'] ?? '',
                ]);

                $order->address()->create([
                    'type' => 'shipping',
                    'firstname' => $order_meta['_shipping_first_name'] ?? '',
                    'lastname' => $order_meta['_shipping_last_name'] ?? '',
                    'address1' => $order_meta['_shipping_address_1'] ?? '',
                    'address2' => $order_meta['_shipping_address_2'] ?? '',
                    'city' => $order_meta['_shipping_city'] ?? '',
                    'state' => $order_meta['_shipping_state'] ?? '',
                    'country' => $order_meta['_shipping_country'] ?? '',
                    'postcode' => $order_meta['_shipping_postcode'] ?? '',
                ]);

                // Order info
                $order->info()->delete();

                $order->info()->create([
                    'email' => $order_meta['_billing_email'],
                    'contact_no' => $order_meta['_billing_phone'],
                    'newsletter' => 1,
                ]);

                // Order items

                $first = true;

                DB::connection('import')
                    ->table('wp_woocommerce_order_items')
                    ->where('order_id', $old_order->ID)
                    ->where('order_item_type', 'line_item')
                    ->get()
                    ->each(function ($old_order_item) use ($order, $order_meta, &$first) {
                        // Fetch order item metadata
                        $order_item_meta = DB::connection('import')
                            ->table('wp_woocommerce_order_itemmeta')
                            ->where('order_item_id', $old_order_item->order_item_id)
                            ->get()
                            ->keyBy('meta_key')
                            ->map(function ($meta) {
                                return $meta->meta_value;
                            })->toArray();

                        if ($order_item_meta['_line_total'] > 0) {

                            $order_item = new Order\Item();
                            // If truncated, no need to call DB
                            if ($this->truncate) {
                                $order_item->id = $old_order_item->order_item_id;
                            } else {
                                $order_item->findOrNew([
                                    'id' => $old_order_item->order_item_id
                                ]);
                            }

                            $product = (new Item)->find($order_item_meta['_product_id']);
                            $size = (new Item\Size)->find($order_item_meta['_variation_id']);


                            $price = $order_item_meta['_line_total'] / $order_item_meta['_qty'];
                            // Get discount per product. Although the discount is applied to order, we'll do this for the first item only.
                            $discount = $first ? ($order_meta['_cart_discount'] ? $order_meta['_cart_discount'] / $order_item_meta['_qty'] : 0) : 0;
                            $price_paid = $price - $discount;

                            $order_item->fill([
                                'order_id' => $old_order_item->order_id,
                                'item_id' => $order_item_meta['_product_id'] ?? 0,
                                'item_name' => $old_order_item->order_item_name ?? '',
                                'item_price' => $price,
                                'item_price_exvat' => $price,
                                'quantity' => $order_item_meta['_qty'],
                                'size' => !empty($size) ? $size->size : '',
                                'PLU' => !empty($product) ? $product->epos_code : '',
                                'discount' => 0,
                                'sizeid' => $order_item_meta['_variation_id'] ?? 0,
                                'vat_deductable' => 0,
                                'price_paid' => $price_paid,
                                'price_paid_exvat' => $price_paid,
                            ]);
                            $order_item->save();

                            // Update order totals
                            $order->cost_total += $order_item_meta['_line_total'];
                            $order->cost_total_exvat += $order_item_meta['_line_total'];
                            $order->save();

                            // Order item meta data. Contains mainly answers to surveys

                            DB::table('order_items_meta')->where('order_item_id', $old_order_item->order_item_id)->delete();

                            foreach ($order_item_meta as $meta_key => $meta_value) {
                                if (!self::ignoreMetaField($meta_key)) {
                                    DB::table('order_items_meta')->insert([
                                        'order_id' => $old_order_item->order_id,
                                        'item_id' => $order_item_meta['_product_id'],
                                        'order_item_id' => $old_order_item->order_item_id,
                                        'meta_key' => $meta_key,
                                        'meta_value' => $meta_value,
                                    ]);
                                }
                            }
                            $first = false;
                        }
                    });

                // Order notes
                $order->notes()->delete();

                DB::connection('import')
                    ->table('wp_comments')
                    ->where('comment_post_ID', $old_order->ID)
                    ->get()
                    ->each(function ($old_order_note) use ($order) {
                        $order->notes()->create([
                            'note' => $old_order_note->comment_content,
                            'timestamp' => $old_order_note->comment_date,
                        ]);
                    });

                return true;
            });

        echo "Orders Imported \n";
    }

    public function importRefunds()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Order Refunds \n";
            DB::table('order_refunds')->truncate();
            echo "Truncating Order Refund Items \n";
            DB::table('order_refund_items')->truncate();
        }
        echo "Importing Refunds \n";

        DB::connection('import')
            ->table('wp_posts')
            ->where('post_type', 'shop_order_refund')
            ->where('post_status', '=', 'wc-completed')
            ->where('post_date', '>=', '2017-06-01 00:00:00')
            ->get()
            ->each(function ($old_refund) {
                $refund_meta = self::getPostMeta($old_refund->ID);

                $to_refund = (float)$refund_meta['_refund_amount'];

                if ($to_refund > 0) {
                    $order = (new Order())->find($old_refund->post_parent);

                    $refund = (new Refund())->create([
                        'order_id' => $order->id,
                        'delivery_refund_amount' => $order->delivery_cost,
                        'reference' => 'RF-' . $order->id,
                        'note' => '',
                        'created_at' => $old_refund->post_date,
                        'updated_at' => $old_refund->post_date,
                    ]);

                    // First refund all items
                    foreach ($order->items as $order_item) {

                        if ($to_refund > 0) {
                            $item_total = $order_item->price_paid * $order_item->quantity;
                            $item_refund = ($item_total - $to_refund > 0) ? $to_refund : $item_total;

                            $refund->items()->create([
                                'order_id' => $order->id,
                                'order_item_id' => $order_item->id,
                                'item_id' => $order_item->item_id,
                                'quantity' => $order_item->quantity,
                                'amount_refunded' => $item_refund,
                                'created_at' => $old_refund->post_date,
                                'updated_at' => $old_refund->post_date,
                            ]);

                            // Subtract the refunded value
                            $to_refund -= $item_refund;
                        }
                    }

                    // Eventually refund the delivery cost
                    $refund->delivery_refund_amount = ($order->delivery_cost - $to_refund > 0) ? $to_refund : $order->delivery_cost;
                    $refund->save();
                }
            });

        echo "Refunds Imported \n";
    }

    /**
     * Import Forms from WordPress
     *
     */
    public function importForms()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Forms \n";
            DB::table('assessment_forms')->truncate();
            DB::table('assessment_questions')->truncate();
        }

        echo "Importing forms... \n";

        DB::connection('import')
            ->table('wp_rg_form')
            ->where('is_trash', '0')
            ->get()
            ->each(function ($old_form) {
                // Order data
                $form = new Form();
                // If truncated, no need to call DB
                if ($this->truncate) {
                    $form->id = $old_form->id;
                } else {
                    $form->findOrNew([
                        'id' => $old_form->id
                    ]);
                }

                // Save Form data
                $form->name = $old_form->title;
                $form->is_active = $old_form->is_active;
                $form->save();

                $form_meta = DB::connection('import')
                    ->table('wp_rg_form_meta')
                    ->where('form_id', $old_form->id)
                    ->first();
                $form_data = json_decode($form_meta->display_meta, true);
                //p($form_data);
                $old_questions = $form_data['fields'] ?? [];
                //pe($old_questions);
                foreach ($old_questions as $old_question) {

                    if (
                        $old_question['type'] === 'hidden' ||
                        $old_question['type'] === 'page' ||
                        $old_question['visibility'] !== 'visible'
                    ) {
                        continue;
                    }

                    $question = new Question();
                    $question->question_text = trim($old_question['label'] . ' ' . ($old_question['description'] ?? ''));
                    $question->old_id = $old_question['id'];
                    $question->is_required = $old_question['isRequired'];
                    $question->question_type = self::$question_type_map[$old_question['type']] ?? $old_question['type'];

                    if ($old_question['type'] === 'radio') {
                        // Determine if this is a yes question
                        if (count($old_question['choices']) === 1 && $old_question['choices'][0]['value'] === 'Yes') {
                            $question->question_type = 'yes';
                            // Determine if this is a yes / no question
                        } elseif (
                            count($old_question['choices']) === 2 &&
                            in_array($old_question['choices'][0]['value'], ['Yes', 'No'], true) &&
                            in_array($old_question['choices'][1]['value'], ['Yes', 'No'], true)
                        ) {
                            $question->question_type = 'yes_no';
                        } else {
                            $question->question_type = self::$question_type_map[$old_question['type']] ?? $old_question['type'];
                        }
                    } elseif ($old_question['type'] === 'html') {
                        $question->question_text = $old_question['content'];
                    }

                    // For select and radio with multiple choices we add variations
                    if ($old_question['type'] === 'select' || ($old_question['type'] === 'radio' && count($old_question['choices']) > 1)) {
                        $variations = [];
                        foreach ($old_question['choices'] as $choice) {
                            if (strcasecmp($choice['text'], 'Select') !== 0) {
                                $variations[] = clean_page(trim($choice['text']));
                            }
                        }
                        $question->variations = json_encode($variations);
                    }

                    $form->questions()->save($question);
                    $question->sort = $question->id;
                    $question->save();
                }

                // Another loop to add conditions
                foreach ($old_questions as $old_question) {
                    if ($question = $form->questions()->where('old_id', $old_question['id'])->first()) {
                        if (!empty($old_question['conditionalLogic']) && $old_question['conditionalLogic']['actionType'] === 'show') {
                            $condition_question = $form
                                ->questions()
                                ->where('old_id', $old_question['conditionalLogic']['rules'][0]['fieldId'])
                                ->first();
                            if (!empty($condition_question)) {
                                $question->show_if_question = $condition_question->id;
                                $question->show_if_answer = $old_question['conditionalLogic']['rules'][0]['value'];
                            }
                        }
                        $form->questions()->save($question);
                    }
                }
            });

        echo "Forms Imported \n";
    }

    /**
     * Import Assessments from WordPress
     *
     */
    public function importAssessments()
    {
        // Check if DB needs truncation
        if ($this->truncate) {
            echo "Truncating Forms \n";
            DB::table('assessments')->truncate();
            DB::table('assessment_answers')->truncate();
        }
        echo "Importing assessments... \n";

        DB::connection('import')
            ->table('wp_rg_lead')
            ->where('status', 'active')
            ->whereNotNull('created_by')
            ->get()
            ->each(function ($old_assessment) {
                DB::table('assessments')->insert([
                    'id' => $old_assessment->id,
                    'member_id' => $old_assessment->created_by,
                    'form_id' => $old_assessment->form_id,
                    'is_completed' => 1,
                    'created_at' => $old_assessment->date_created,
                    'updated_at' => $old_assessment->date_created,
                ]);
                DB::connection('import')
                    ->table('wp_rg_lead_detail')
                    ->where('lead_id', $old_assessment->id)
                    ->get()
                    ->each(function ($old_answer) use ($old_assessment) {
                        if ($question_id = Question::query()
                            ->where('old_id', $old_answer->field_number)
                            ->where('form_id', $old_answer->form_id)
                            ->value('id')
                        ) {
                            $answer_long = DB::connection('import')
                                ->table('wp_rg_lead_detail_long')
                                ->where('lead_detail_id', $old_answer->id)
                                ->value('value');
                            DB::table('assessment_answers')->insert([
                                'assessment_id' => $old_answer->lead_id,
                                'form_id' => $old_answer->form_id,
                                'question_id' => $question_id,
                                'answer' => $answer_long ?? $old_answer->value,
                            ]);
                        }
                    });
            });

        echo "Assessments Imported \n";
    }

    /**
     * Tries to locate member duplicates and merges them
     */
    public function mergeMemberDuplicates() {
        Member::query()
            // Keep the most recent member
            ->orderBy('id', 'desc')
            ->get()
            ->each(function ($member) {

                $parent_member = Member::getDuplicate($member);

                if (!empty($parent_member)) {

                    // Move all orders from the duplicate member to parent member
                    Order::query()
                        ->where('member', $member->id)
                        ->update([
                            'member' => $parent_member->id
                        ]);

                    // Update email as well
                    Order\Info::query()
                        ->where('email', $member->email)
                        ->update([
                            'email' => $parent_member->email
                        ]);

                    echo "Member deleted ID: {$member->id} , email: {$member->email} \n";

                    // Remove the duplicate member
                    $member->delete();
                }
            });
    }

    public function loadRemoteImages()
    {
        (new Item\Image)
            ->where('old_location', '<>', '')
            ->get()
            ->each(function ($item_image) {
                foreach ($this->image_folders['product_folders'] as $folder_name => $resize_params) {
                    $this->saveRemoteImage($item_image->old_location, $item_image->name, $resize_params);
                }
            });
    }

    private function saveRemoteImage($old_location, $new_name, $resize_params)
    {
        $image_url = 'https://www.cloudpharmacy.co.uk/' . $old_location;
        copy_image($image_url, $new_name, $resize_params);
    }

    private static function getStatus($WPStatus)
    {
        return self::$status_map[$WPStatus] ?? 0;
    }

    private static function ignoreMetaField($meta_key)
    {
        if (in_array($meta_key, [
            '_qty',
            '_tax_class',
            '_product_id',
            '_variation_id',
            '_line_subtotal',
            '_line_total',
            '_line_subtotal_tax',
            '_line_tax',
            '_line_tax_data',
            '_gravity_forms_history',
            '_Product display',
            'Agree to terms and conditions',
            '_wc_cog_item_cost',
            '_wc_cog_item_total_cost',
            '_Display name (hidden)',
            '_Email (hidden)',
            'quantity',
            '_Existing patient username'
        ])) {
            return true;
        }
        return false;
    }

    private static function getPostMeta($post_id) {
        return DB::connection('import')
            ->table('wp_postmeta')
            ->where('post_id', $post_id)
            ->get()
            ->keyBy('meta_key')
            ->map(function ($meta) {
                return $meta->meta_value;
            })->toArray();
    }

    private static function remoteURLExists($url) {
        $handle = curl_init($url);
        curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);
        curl_exec($handle);
        $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            curl_close($handle);
            return false;
        }

        curl_close($handle);
        return true;
    }

    /**
     * The patch function is the general run function for patches, so that we don't have to define the functions in settings.
     */
    public function patch()
    {
        $this->patchImportMissingFormQuestions();
    }

    /******************************************************************************************************************
     *
     *                                          Below go all the patch functions
     *
     ******************************************************************************************************************/

    public function patchImportMissingFormQuestions()
    {
        echo "Importing forms... <br />\n";

        DB::connection('import')
            ->table('wp_rg_form')
            ->where('is_trash', '0')
            ->get()
            ->each(function ($old_form) {
                // Order data
                $form = (new Form())->find($old_form->id);

                if ($form->questions()->count() > 0) {
                    return;
                }
                echo $form->id . '<br />';

                // Save Form data
                $form->name = $old_form->title;
                $form->is_active = $old_form->is_active;
                $form->save();

                $form_meta = DB::connection('import')
                    ->table('wp_rg_form_meta')
                    ->where('form_id', $old_form->id)
                    ->first();
                $form_data = json_decode($form_meta->display_meta, true);
                //p($form_data);
                $old_questions = $form_data['fields'] ?? [];
                //pe($old_questions);

                foreach ($old_questions as $old_question) {

                    if (
                        $old_question['type'] === 'hidden' ||
                        $old_question['type'] === 'page'
                    ) {
                        continue;
                    }

                    $question = new Question();
                    $question->question_text = trim($old_question['label'] . ' ' . ($old_question['description'] ?? ''));
                    $question->old_id = $old_question['id'];
                    $question->is_required = $old_question['isRequired'];
                    $question->question_type = self::$question_type_map[$old_question['type']] ?? $old_question['type'];

                    if ($old_question['type'] === 'radio') {
                        // Determine if this is a yes question
                        if (count($old_question['choices']) === 1 && $old_question['choices'][0]['value'] === 'Yes') {
                            $question->question_type = 'yes';
                            // Determine if this is a yes / no question
                        } elseif (
                            count($old_question['choices']) === 2 &&
                            in_array($old_question['choices'][0]['value'], ['Yes', 'No'], true) &&
                            in_array($old_question['choices'][1]['value'], ['Yes', 'No'], true)
                        ) {
                            $question->question_type = 'yes_no';
                        } else {
                            $question->question_type = self::$question_type_map[$old_question['type']] ?? $old_question['type'];
                        }
                    } elseif ($old_question['type'] === 'html') {
                        $question->question_text = $old_question['content'];
                    }

                    // For select and radio with multiple choices we add variations
                    if ($old_question['type'] === 'select' || ($old_question['type'] === 'radio' && count($old_question['choices']) > 1)) {
                        $variations = [];
                        foreach ($old_question['choices'] as $choice) {
                            if (strcasecmp($choice['text'], 'Select') !== 0) {
                                $variations[] = clean_page(trim($choice['text']));
                            }
                        }
                        $question->variations = json_encode($variations);
                    }

                    $form->questions()->save($question);
                    $question->sort = $question->id;
                    $question->save();
                }

                // Another loop to add conditions
                foreach ($old_questions as $old_question) {
                    if ($question = $form->questions()->where('old_id', $old_question['id'])->first()) {
                        if (!empty($old_question['conditionalLogic']) && $old_question['conditionalLogic']['actionType'] === 'show') {
                            $condition_question = $form
                                ->questions()
                                ->where('old_id', $old_question['conditionalLogic']['rules'][0]['fieldId'])
                                ->first();
                            if (!empty($condition_question)) {
                                $question->show_if_question = $condition_question->id;
                                $question->show_if_answer = $old_question['conditionalLogic']['rules'][0]['value'];
                            }
                        }
                        $form->questions()->save($question);
                    }
                }

            });

        echo "Forms Imported \n";
    }
}