<?php

declare(strict_types=1);

use Cake\TestSuite\TestCase;
use Cake\Datasource\ConnectionManager;
use Cake\Http\Client;
use Cake\Http\Client\Response;

class TestStripeModel extends StripeAppModel
{
    public string $path = '/action';
}

class StripeSourceTest extends TestCase
{
    private StripeSource $Source;
    private TestStripeModel $Model;

    public function setUp(): void
    {
        parent::setUp();
        ConnectionManager::loadDatasource([
            'plugin' => 'Stripe',
            'classname' => 'StripeSource'
        ]);

        $this->Source = new StripeSource([
            'api_key' => '123456'
        ]);

        $this->Source->Http = $this->createMock(Client::class);
        $this->Model = new TestStripeModel();
    }

    public function tearDown(): void
    {
        parent::tearDown();
        unset($this->Source, $this->Model);
    }

    public function testReformat(): void
    {
        $model = new StripeCustomer();

        $data = [
            'number' => '234',
            'name' => 'Jeremy',
            'email' => 'jeremy@42pixels.com',
            'address_line_1' => '123 Main'
        ];
        $result = $this->Source->reformat($model, $data);
        $expected = [
            'card' => [
                'number' => '234',
                'name' => 'Jeremy',
                'address_line_1' => '123 Main'
            ],
            'email' => 'jeremy@42pixels.com'
        ];
        $this->assertEquals($expected, $result);

        $model->formatFields['user'] = ['email'];
        $result = $this->Source->reformat($model, $data);
        $expected = [
            'card' => [
                'number' => '234',
                'name' => 'Jeremy',
                'address_line_1' => '123 Main'
            ],
            'user' => [
                'email' => 'jeremy@42pixels.com'
            ]
        ];
        $this->assertEquals($expected, $result);
    }

    public function testConstructWithoutKey(): void
    {
        $this->expectException(CakeException::class);
        new StripeSource();
    }

    public function testRequest(): void
    {
        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 404,
            'body' => '{}'
        ]));

        $response = $this->Source->request(['uri' => ['path' => '/path/']]);
        $this->assertFalse($response);
        $this->assertEquals('Unexpected error.', $this->Source->lastError);
        $this->assertEquals('/v1/path', $this->Source->request['uri']['path']);

        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 402,
            'body' => '{"error":{ "message" : "This is an error message"}}'
        ]));

        $response = $this->Source->request();
        $this->assertFalse($response);
        $this->assertEquals('This is an error message', $this->Source->lastError);

        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 200,
            'body' => '{"id" : "123"}'
        ]));

        $response = $this->Source->request();
        $this->assertNull($this->Source->lastError);
        $this->assertEquals(['id' => '123'], $response);
    }

    public function testCreate(): void
    {
        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 200,
            'body' => '{"object" : "customer", "id" : "1234"}'
        ]));

        $response = $this->Source->create($this->Model, ['email', 'description'], ['jeremy@42pixels.com', 'Jeremy Harris']);
        $this->assertTrue($response);
        $this->assertEquals('POST', $this->Source->request['method']);
        $this->assertEquals(1234, $this->Model->getLastInsertId());
        $this->assertEquals([
            'email' => 'jeremy@42pixels.com',
            'description' => 'Jeremy Harris'
        ], $this->Source->request['body']);
    }

    public function testRead(): void
    {
        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 200,
            'body' => '{"object" : "customer", "id" : "1234", "description" : "Jeremy Harris"}'
        ]));

        $response = $this->Source->read($this->Model, [
            'conditions' => ['TestStripeModel.id' => '1234'],
            'fields' => [],
        ]);

        $this->assertEquals([
            ['TestStripeModel' => [
                'id' => '1234',
                'object' => 'customer',
                'description' => 'Jeremy Harris'
            ]]
        ], $response);
        $this->assertEquals(1234, $this->Model->id);
        $this->assertEquals('GET', $this->Source->request['method']);
        $this->assertEquals('/v1/action/1234', $this->Source->request['uri']['path']);
    }

    public function testUpdate(): void
    {
        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 200,
            'body' => '{"object" : "customer", "id" : "1234"}'
        ]));

        $response = $this->Source->update($this->Model, ['email', 'description', 'id'], ['jeremy@42pixels.com', 'Jeremy Harris', '1234']);

        $this->assertEquals([
            'TestStripeModel' => [
                'object' => 'customer',
                'id' => '1234',
            ]
        ], $response);
        $this->assertEquals(1234, $this->Model->id);
        $this->assertEquals([
            'email' => 'jeremy@42pixels.com',
            'description' => 'Jeremy Harris'
        ], $this->Source->request['body']);
        $this->assertEquals('POST', $this->Source->request['method']);
        $this->assertEquals('/v1/action/1234', $this->Source->request['uri']['path']);
    }

    public function testDelete(): void
    {
        $this->Source->Http->method('request')->willReturn(new Response([
            'statusCode' => 200,
            'body' => '{"deleted" : "true", "id" : "1234"}'
        ]));

        $response = $this->Source->delete($this->Model, ['TestStripeModel.id' => '1234']);
        $this->assertTrue($response);
        $this->assertEquals('DELETE', $this->Source->request['method']);
        $this->assertEquals('/v1/action/1234', $this->Source->request['uri']['path']);
    }
}