<?php

declare(strict_types=1);

use Crossroads\Magento\Test\Integration\MagentoManager;
use Crossroads\Magento\Test\Integration\Request;
use GraphQL\Utils\SchemaPrinter;
use PHPUnit\Framework\TestCase;
use Spatie\Snapshots\Driver;
use Spatie\Snapshots\MatchesSnapshots;

class PayoneTest extends TestCase implements Driver
{    
    use MatchesSnapshots;

    public function setUp(): void {
        MagentoManager::reset();
        MagentoManager::init('default');
    }

    public function tearDown(): void {
        MagentoManager::logQueries();
    }

    public function serialize($data): string {
        return $data;
    }

    public function extension(): string {
        return "graphql";
    }

    public function match($expected, $actual): void {
        Assert::assertEquals($expected, $this->serialize($actual));
    }

    // Test listing available methods
    public function testMethodListing(): void {
        $store = Mage::app()->getStore();
        $product = $this->getProduct($store);
        $quote = $this->getQuote($product);

        // List all payment methods
        $response = $this->runQuery("query { paymentMethods { code title } }");

        $this->assertEquals(200, $response->getHttpResponseCode());
        $this->assertEquals("application/json; charset=utf-8", $response->getHeader("Content-Type"));
        $this->assertMatchesJsonSnapshot($response->getBody());
    }

    // Test set method schema
    public function testSchema(): void {
        $store = Mage::app()->getStore();

        // List all payment methods
        $response = $this->runQuery(
            '{ __type(name: "MutationSetQuotePaymentMethod") { name fields { name type { name fields { name type { name } } } } } }'
        );

        $this->assertEquals(200, $response->getHttpResponseCode());
        $this->assertEquals("application/json; charset=utf-8", $response->getHeader("Content-Type"));
        $this->assertMatchesJsonSnapshot($response->getBody());
    }

    public function provideMethods(): array {
        $store = Mage::app()->getStore();
        $provides = [];        
        foreach ($this->initPayone($store) as $payone_method) {
            $provides[] = [$payone_method];
        }
        return $provides;
    }
    
    /**
     * @dataProvider provideMethods
     */
    public function testValidUri($payone_method): void
    {
        $store = Mage::app()->getStore();
        $product = $this->getProduct($store);
        $quote = $this->getQuote($product);

        // Set payment method
        $response = $this->runQuery(
            "mutation { setQuotePaymentMethod { {$payone_method} { result } } }"
        );

        $this->assertEquals(200, $response->getHttpResponseCode());
        $this->assertEquals("application/json; charset=utf-8", $response->getHeader("Content-Type"));
        $this->assertMatchesJsonSnapshot($response->getBody());

        MagentoManager::reset();
        MagentoManager::init('default');
        Mage::getSingleton("checkout/session")->setQuoteId($quote->getId());

        // Check result
        $response = $this->runQuery(
            "query { quote { email payment { code } } }"
        );

        $this->assertEquals(200, $response->getHttpResponseCode());
        $this->assertEquals("application/json; charset=utf-8", $response->getHeader("Content-Type"));
        $this->assertMatchesJsonSnapshot($response->getBody());
    }

    private function runQuery(string $query) {
        return MagentoManager::runRequest(new Request("POST /graphql", [
            "Content-Type" => "application/graphql",
        ], $query), 'default');
    }

    private function getProduct(Mage_Core_Model_Store $store): Mage_Catalog_Model_Product {
        $product = Mage::getModel("catalog/product");
        $product->setStoreId($store->getId())
            ->load(Mage::getModel("catalog/product")->getIdBySku("test-simple"));
        return $product;
    }

    // Create a Quote - Fake it just enough to pass Payone validity tests
    private function getQuote(Mage_Catalog_Model_Product ...$products): Mage_Sales_Model_Quote {
        $quote = Mage::getSingleton("checkout/session")->getQuote();
        $quote->setBillingAddress(Mage::getModel("sales/quote_address")->addData([
            "country_id" => "DE",
        ]));
        $quote->setShippingAddress(Mage::getModel("sales/quote_address")->addData([
            "country_id" => "DE",
        ]));
        $quote->setQuoteCurrencyCode('EUR');
        $quote->setCustomerEmail('test@awardit.com');
        $options = new Varien_Object(["qty" => 1]);
        foreach ($products as $product) {
            $quote->addProduct($product, $options);
        }
        $quote->setTotalsCollectedFlag(false);
        $quote->getShippingAddress()->setCollectShippingRates(true);
        $quote->collectTotals();
        $quote->save();
        return $quote;
    }

    // Payone need to be setup before it can be used
    private function initPayone(Mage_Core_Model_Store $store): array {
        // Ugly hack, as Payone expects these to enable their methods
        $_POST['groups']['template_apple_pay']['fields']['apl_merchant_identification_certificate']['value'] = null;
        $_POST['groups']['template_apple_pay']['fields']['apl_certificate_private_key']['value'] = null;
        // This will fail if not properly set up
        $skip = ['payone_safe_invoice'];
        $config = new Mage_Core_Model_Config();
        $active = [];
        foreach ($store->getConfig("mageql/sales/payment") as $code => $data) {
            if (substr($code, 0, 7) == "payone_" && !in_array($code, $skip)) {
                $config->saveConfig("payment/{$code}/active", "1", "default", 0);
                $pm_config = Mage::getModel("payone_core/domain_config_paymentMethod");
                $pm_config->setCode(substr($code, 7));
                $pm_config->setEnabled(1);
                $pm_config->setMode('test');
                $pm_config->setName("Test for {$code}");
                $pm_config->save();
                $active[] = $code;
            }
        }
        $initializer = Mage::getModel("payone_core/service_initializeConfig");
        $initializer->execute($store->getId(), false);
        return $active;
    }
}
