<?php
/**
 * @copyright   Copyright (c) 2023 TheMarketer.com
 * @project     TheMarketer.com
 * @website     https://themarketer.com/
 * @author      TheMarketer
 * @license     http://opensource.org/licenses/osl-3.0.php - Open Software License (OSL 3.0)
 * @docs        https://themarketer.com/resources/api
 */

namespace Mktr\Tracker\Model;

use Magento\Customer\Model\GroupFactory;
use Magento\SalesRule\Helper\Coupon;
use Magento\SalesRule\Model\Coupon\CodegeneratorInterface;
use Magento\SalesRule\Model\Rule;
use Magento\SalesRule\Model\RuleFactory;
use Magento\Store\Api\StoreRepositoryInterface;

class DiscountCode extends \Magento\Framework\DataObject implements CodegeneratorInterface
{
    const discountRules = [
        0 => "fixedValue",
        1 => "percentage",
        2 => "freeShipping"
    ];

    const PREFIX = 'MKTR-';
    const NAME = "MKTR-%s-%s";
    const DESCRIPTION = "Discount Code Generated through TheMarketer API";

    /**
     * @var array
     */
    private $newCode = [];

    /**
     * @var self|null
     */
    private $generator;

    /**
     * @var array|null
     */
    private $customerGroups;

    /**
     * @var array|null
     */
    private $websiteIds;

    /**
     * @var RuleFactory
     */
    private $ruleFactory;

    /**
     * @var GroupFactory
     */
    private $customerGroupFactory;

    /**
     * @var StoreRepositoryInterface
     */
    private $storeRepository;

    /**
     * @var string|null
     */
    private $ruleType;

    public function __construct(
        RuleFactory $ruleFactory,
        GroupFactory $customerGroupFactory,
        StoreRepositoryInterface $storeRepository,
        array $data = []
    ) {
        parent::__construct($data);
        $this->ruleFactory = $ruleFactory;
        $this->customerGroupFactory = $customerGroupFactory;
        $this->storeRepository = $storeRepository;
    }

    private function getGenerator()
    {
        if ($this->generator === null) {
            $this->generator = $this;
            $this->generator->setFormat(Coupon::COUPON_FORMAT_ALPHANUMERIC);
            $this->generator->setLength(10);
            $this->generator->setPrefix(self::PREFIX);
            $this->generator->setType(Coupon::COUPON_TYPE_SPECIFIC_AUTOGENERATED);
        }
        return $this->generator;
    }

    public function getNewCode($p)
    {
        $this->ruleType = self::discountRules[$p['type']];

        $name = vsprintf(self::NAME, [
                $this->ruleType,
                $p['value']
            ]) . (isset($p['expiration_date']) ? '-' . $p['expiration_date'] : '');

        $existingRule = $this->ruleFactory->create()
            ->getCollection()
            ->addFieldToFilter('name', ['eq' => $name])
            ->getFirstItem();

        if ($existingRule === null || !$existingRule->getRuleId()) {
            $rule = $this->ruleFactory->create();

            if ($this->websiteIds === null) {
                $nIds = [];
                foreach ($this->storeRepository->getList() as $website) {
                    if ($website->getCode() !== 'admin' && !in_array($website->getWebsiteId(), $nIds)) {
                        $nIds[] = $website->getWebsiteId();
                    }
                }
                $this->websiteIds = $nIds;
            }
            if ($this->customerGroups === null) {
                $nGroups = [];
                foreach ($this->customerGroupFactory->create()->getCollection()->toOptionHash() as $groupId => $n) {
                    if (!in_array($groupId, $nGroups)) {
                        $nGroups[] = $groupId;
                    }
                }
                $this->customerGroups = $nGroups;
            }

            $rule->setCustomerGroupIds($this->customerGroups);
            $rule->setWebsiteIds($this->websiteIds);

            $rule->setName($name)
                ->setDescription(self::DESCRIPTION)
                ->setStopRulesProcessing(0)
                ->setFromDate(date('Y-m-d', strtotime(date('Y-m-d') . ' -1 day')))
                ->setIsActive(1)
                ->setUsesPerCoupon(1)
                ->setUsesPerCustomer(1)
                ->setSortOrder(0)
                ->setDiscountAmount($p['value'])
                ->setDiscountQty(0)
                ->setDiscountStep(0)
                ->setApplyToShipping(0)
                ->setIsRss(0)
                ->setUseAutoGeneration(true);

            switch ($this->ruleType) {
                case 'percentage':
                    $rule->setSimpleAction(Rule::BY_PERCENT_ACTION);
                    break;
                case 'freeShipping':
                    $rule->setSimpleAction(Rule::BY_PERCENT_ACTION);
                    $rule->setSimpleFreeShipping(1);
                    break;
                case 'fixedValue':
                    $rule->setSimpleAction(Rule::CART_FIXED_ACTION);
                    break;
            }

            $rule->setToDate($p['expiration_date'] ?? '');
            $rule->setCouponCodeGenerator($this->getGenerator());
            $rule->setCouponType(Rule::COUPON_TYPE_AUTO);
            $rule->setUseAutoGeneration(Coupon::COUPON_TYPE_SPECIFIC_AUTOGENERATED);
            $rule->save();
            $this->newCode[$name] = $rule;
        } else {
            $this->newCode[$name] = $existingRule;
            $this->newCode[$name]->setCouponCodeGenerator($this->getGenerator());
            $this->newCode[$name]->setCouponType(Rule::COUPON_TYPE_AUTO);
        }
        $this->newCode[$name]->acquireCoupon(true);
        return $this->newCode[$name];
    }

    const DEFAULT_LENGTH_MIN = 16;
    const DEFAULT_LENGTH_MAX = 32;
    const SYMBOLS_COLLECTION = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    const DEFAULT_DELIMITER = '-';

    private $code = null;

    public function generateCode()
    {
        $alphabet = $this->getAlphabet() ? $this->getAlphabet() : static::SYMBOLS_COLLECTION;
        $length = $this->getActualLength();

        $this->code = $this->getPrefix() . '';
        for ($i = 0, $indexMax = strlen($alphabet) - 1; $i < $length; ++$i) {
            $this->code .= substr($alphabet, random_int(0, $indexMax), 1);
        }

        return $this->code;
    }

    public function getCode()
    {
        return $this->code;
    }

    protected function getActualLength()
    {
        $lengthMin = $this->getLengthMin() ? $this->getLengthMin() : static::DEFAULT_LENGTH_MIN;
        $lengthMax = $this->getLengthMax() ? $this->getLengthMax() : static::DEFAULT_LENGTH_MAX;

        return $this->getLength() ? $this->getLength() : random_int($lengthMin, $lengthMax);
    }

    public function getDelimiter()
    {
        return $this->hasData('delimiter') ? $this->getData('delimiter') : static::DEFAULT_DELIMITER;
    }
}
