<?php

namespace MtcPharmacy\Subscriptions\Console\Commands;

use Mtc\Shop\Order;
use App\SubscriptionBundleType;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Mtc\Shop\Assessment\Assessment;
use MtcPharmacy\Subscriptions\Classes\Subscription;
use MtcPharmacy\Subscriptions\Classes\SubscriptionLog;
use MtcPharmacy\Subscriptions\Classes\SubscriptionItem;
use MtcPharmacy\Subscriptions\Classes\SubscriptionPreference;
use MtcPharmacy\Subscriptions\Classes\Services\PaymentService;
use MtcPharmacy\Subscriptions\Classes\PaymentGateways\RyftPaymentGateway;
use MtcPharmacy\Subscriptions\Classes\PaymentGateways\TestPaymentGateway;



/**
 * Examples:
 *     php artisan subscriptions:generate_from_orders
 */


class GenerateSubscriptionsCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'subscriptions:generate_from_orders';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate subscription from paid orders that include items which have requested this service.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->info(__METHOD__);
        $order_ids = $this->get_orders_to_process();
        $this->info('Found the following orders to process: ' . $order_ids->toJson());

        if (count($order_ids) > 0) {
            Log::info("Subscription:-> Processing orders: " . $order_ids->toJson());
            foreach ($order_ids as $i => $order_id) {
                $this->info("Processing order id:{$order_id}");

                $order = Order::find($order_id);

                $order_items_grouped = $this->get_order_items_grouped($order);

                $this->info("* Unbundled items:");
                foreach ($order_items_grouped['unbundled'] as $order_item) {
                    $this->info(" - order item id:{$order_item->id}");
                    if ($order_item->has_requested_subscription) {
                        $sub = $this->create_new_subscription($order);
                        $this->add_item_to_subscription($order_item, $sub);
                        $sub->orders()->attach($order);
                        $this->adjust_subscription_interval($sub, $order_item);
                        $this->adjust_subscription_expiry_date($sub, $order_item);
                    }
                }

                $this->info("* Bundled items:");
                foreach ($order_items_grouped['bundled'] as $bundle_id => $bundle_items) {
                    $this->info(" - bundle id:{$bundle_id}");

                    $sub = $this->create_new_subscription($order);
                    foreach ($bundle_items as $bundle_item) {
                        $this->add_item_to_subscription($bundle_item, $sub);
                    }
                    $sub->orders()->attach($order);
                    SubscriptionBundleType::relate($sub, $bundle_item->bundle->type);
                    $this->adjust_subscription_interval($sub, $bundle_item);
                    $this->adjust_subscription_expiry_date($sub, $bundle_item);
                }

            }
        }


        $this->info('Done.');
    }



    /*** HELPERS ***/

    private function get_orders_to_process()
    {
        $query_builder = DB::table('order_items AS oi')
            ->selectRaw('DISTINCT oi.order_id')
            ->join('order AS o', 'o.id', '=', 'oi.order_id')
            ->leftJoin('subscriptions_orders AS so', 'so.order_id', '=', 'oi.order_id')
            ->where('o.paid', 1)
            ->where('o.has_subscription', 1)
            ->where('oi.has_requested_subscription', 1)
            ->whereNull('so.id')
        ;

        $order_ids = $query_builder->pluck('order_id');

        return $order_ids;
    }


    private function add_item_to_subscription($container_item, $sub)
    {
        if (!$container_item->item) {
            $this->info(" -- skipping item (product not found)");
            return;
        }

        $si = SubscriptionItem::createFromShopItem($container_item->item, $container_item->item_size);
        $si->quantity = $container_item->quantity;
        $si->assessment_id = $container_item->assessment_id;
        $si->subscription()->associate($sub);
        $si->save();

        $shop_item_size_id = $container_item->item_size->id ?? '-';
        $this->info(" -- added shop item id:{$container_item->item->id}|{$shop_item_size_id}");
    }


    private function create_new_subscription($order)
    {
        $sub = new Subscription();
        $sub->status = Subscription::STATUS_ACTIVE;
        $sub->member()->associate($order->member);
        $sub->save();

        // Handle subscription creation log
        SubscriptionLog::logCreated($sub);

        $this->info(" -- created subscripton id:{$sub->id}");

        return $sub;
    }


    private function get_order_items_grouped($order)
    {
        $order_items_grouped = [
            'bundled' => [],
            'unbundled' => [],
        ];

        foreach ($order->items as $order_item) {
            if ($order_item->bundle) {
                if (!isset($order_items_grouped['bundled'][$order_item->bundle->id])) {
                    $order_items_grouped['bundled'][$order_item->bundle->id] = [];
                }
                $order_items_grouped['bundled'][$order_item->bundle->id][] = $order_item;
            } else {
                $order_items_grouped['unbundled'][] = $order_item;
            }
        }

        return $order_items_grouped;
    }


    private function adjust_subscription_interval($subscription, $order_item)
    {
        $this->info(" -- parsing period: {$order_item->has_requested_subscription_period}");

        if (empty($order_item->has_requested_subscription_period)) {
            $order_item->has_requested_subscription_period = '1 months';
        }

        $sub_period_segments = explode(' ', $order_item->has_requested_subscription_period);

        if (count($sub_period_segments) != 2) {
            throw new \Exception('Missing subscription period.');
        }

        $temp_interval_multiplier = (int) $sub_period_segments[0];
        if ($temp_interval_multiplier <= 0) {
            throw new \Exception('Subscription interval multiplier needs to be a positive integer.');
        }

        $temp_interval = null;
        switch (trim($sub_period_segments[1])) {
            case 'day':
            case 'days':
                $temp_interval = Subscription::INTERVAL_DAYS;
                break;
            case 'week':
            case 'weeks':
                $temp_interval = Subscription::INTERVAL_WEEKS;
                break;
            case 'month':
            case 'months':
                $temp_interval = Subscription::INTERVAL_MONTHS;
                break;
        }

        if (!$temp_interval) {
            throw new \Exception('Subscription interval invalid string.');
        }

        $subscription->interval = $temp_interval;
        $subscription->interval_multiplier = $temp_interval_multiplier;

        $subscription->next_issue_date = Subscription::calculateNextIssueDate($subscription->created_at, $subscription->interval, $subscription->interval_multiplier);


        $sub_config = SubscriptionPreference::init($order_item->item->id, $order_item->item_size->id ?? null);

        if ($sub_config) {
            $subscription->max_number_of_issues = $sub_config->max_number_of_issues;
            $subscription->can_be_paused = $sub_config->can_be_paused;
        }

        $subscription->save();
    }


    private function adjust_subscription_expiry_date($subscription, $order_item)
    {
        if (!$order_item->item)
            return;

        $expiryDate = date('Y-m-d', strtotime('+1 year'));

        try {
            $assessment = Assessment::getCompletedForItem($order_item->item, $order_item->order->customer);
            if ($assessment) {
                $expiryDate = $assessment->getExpiryDate(365);
            }
        } catch (\Exception $exception) {
            $this->info($exception->getMessage());
        }

        $subscription->expiry_date = $expiryDate;
        $subscription->save();

    }
}
