<?php

declare(strict_types=1);

use GraphQL\Type\Definition\ResolveInfo;

use MageQL\Registry;
use MageQL\Type\AbstractBuilder;
use MageQL\Type\FieldBuilder;
use MageQL\Type\InputFieldBuilder;

abstract class Awardit_Payone_Model_Payment_Abstract extends MageQL_Sales_Model_Payment_Abstract {
    /**
     * Payment method was set successfully.
     */
    const SUCCESS = "success";
    const ERROR_NOT_AVAILABLE = "errorNotAvailable";
    const ERROR_NOT_ALLOWED_FOR_BILLING_COUNTRY = "errorCountryNotAllowed";

    /**
     * Returns the payment method code.
     */
    abstract public function getCode(): string;

    /**
     * Returns the type name for the payment type.
     */
    public function getMethodTypeName(): string
    {
        return ucfirst(preg_replace_callback('/(_[a-z0-9])/', function ($matches) {
            return trim(strtoupper($matches[1]), '_');
        }, $this->getCode()));
    }

    /**
     * Creates the FieldBuilder for the payment method, override and modify the
     * return value with extra arguments if necessary.
     * @psalm-suppress PossiblyUnusedParam
     */
    public function getQuotePaymentMutationFieldBuilder(Registry $registry): FieldBuilder {
        return $this->field("MutationSetQuotePaymentMethodPayone", "Updates the quote to use {$this->getCode()}.")
            ->setResolver([$this, "mutateSetPaymentMethod"]);
    }

    /**
     * Type name for object containing information about the available payment method.
     */
    public function getPaymentMethodType(): string {
        return "PaymentMethod{$this->getMethodTypeName()}";
    }

    /**
     * Type name for object containing information about the selected payment method.
     */
    public function getQuotePaymentType(): string {
        return "QuotePayment{$this->getMethodTypeName()}";
    }

    /**
     * Creates the type builder for the active quote payment information when using this payment method.
     */
    public function getQuotePaymentTypeBuilder(): AbstractBuilder {
        $methodName = $this->getMethodTypeName();

        return $this->object("Selected $methodName payment data")
            ->setInterfaces(["QuotePayment"])
            ->setResolveField("MageQL\\defaultVarienObjectResolver");
    }

    /**
     * Creates the type builder for the payment method information.
     */
    public function getPaymentMethodTypeBuilder(): AbstractBuilder {
        return $this->object("{$this->getCode()} payment method data")
            ->setInterfaces(["PaymentMethod"])
            ->setResolveField("MageQL\\defaultVarienObjectResolver");
    }

    public function getMutationBuilder(): AbstractBuilder {
        return $this->object("The result of attempting to set the quote payment method to {$this->getCode()}");
    }

    public function getMutationResultBuilder(): AbstractBuilder {
        return $this->enum("Result of a SetQuotePaymentMethod Payone mutation", $this->getMutationResultErrors());
    }

    /**
     * @psalm-suppress PossiblyUnusedParam
     */
    public function getTypeBuilder(string $typeName, Registry $registry): ?AbstractBuilder {
        switch($typeName) {

        case "MutationSetQuotePaymentMethodPayone":
            return $this->getMutationBuilder();
            
        case "MutationSetQuotePaymentMethodPayoneResult":
            return $this->getMutationResultBuilder();

        case $this->getPaymentMethodType():
            return $this->getPaymentMethodTypeBuilder();

        case $this->getQuotePaymentType():
            return $this->getQuotePaymentTypeBuilder();

        default:
            return null;
        }
    }

    /**
     * @return Array<string, FieldBuilder|InputFieldBuilder>
     */
    public function getTypeFields(string $typeName, Registry $registry): array {
        switch($typeName) {
        case "MutationSetQuotePaymentMethod":
            return $this->isAvailable()
                ? [$this->getCode() => $this->getQuotePaymentMutationFieldBuilder($registry)]
                : [];
        case $this->getPaymentMethodType():
            return array_merge(
                $registry->getFieldBuilders("PaymentMethod"),
                $this->getPaymentMethodFields($registry)
            );
        case $this->getQuotePaymentType():
            return array_merge(
                $registry->getFieldBuilders("QuotePayment"),
                $this->getQuotePaymentFields($registry)
            );
        case "MutationSetQuotePaymentMethodPayone":
            return $this->getMutateSetQuotePaymentFields($registry);
        default:
            return [];
        }
    }

    public function getUnreachableTypes(): array {
        return [
            $this->getQuotePaymentType(),
            $this->getPaymentMethodType(),
        ];
    }

    /**
     * @return Array<FieldBuilder>
     */
    public function getPaymentMethodFields(Registry $registry): array {
        return [];
    }

    /**
     * @return Array<FieldBuilder>
     */
    public function getQuotePaymentFields(Registry $registry): array {
        return [];
    }

    /**
     * @return Array<FieldBuilder>
     */
    public function getMutateSetQuotePaymentFields(Registry $registry): array {
        return [
            "result" => $this->field(
                "MutationSetQuotePaymentMethodPayoneResult!",
                "Result of attempting to set the payment method to a Payone method"
            )
                // Simple result, we either succeed or fail
                ->setResolver("MageQL\\forwardResolver"),
        ];
    }

    /**
     * Set payment method on quote
     * @return string
     */
    public function mutateSetPaymentMethod(Mage_Sales_Model_Quote $quote, array $args, MageQL_Core_Model_Context $ctx, ResolveInfo $info) {
        $model = Mage::getSingleton("mageql_sales/quote");
        try {
            Mage::helper("mageql_sales/quote")->importPaymentData($quote, $this->getCode(), []);
            $model->saveSessionQuote();
            $quote->getPayment()->save();

            return self::SUCCESS;
        }
        catch(Mage_Core_Exception $e) {
            return $this->translatePaymentError($e);
        }
    }

    /**
     * Get payment method
     * @return Payone_Core_Model_Payment_Method_Abstract Payment method
     */
    protected function getMethodInstance(): Payone_Core_Model_Payment_Method_Abstract {
        $instance = Mage::helper("payment")->getMethodInstance($this->getCode());
        if (empty($instance)) {
            throw new RuntimeException("Could not find payment method instance");
        }
        return $instance;
    }

    /**
     * Check if payment method is available
     * @param Mage_Sales_Model_Quote|null $quote Quoute to check
     * @return bool Availability
     */
    protected function isAvailable(?Mage_Sales_Model_Quote $quote = null): bool {
        return $this->getMethodInstance()->isAvailable($quote);
    }

}
