<?php

namespace classes;

use Stripe\HttpClient\ClientInterface;

class stripe_fake_http_client implements ClientInterface
{
    private const DEFAULT_STATE = [
        'next_customer' => 1,
        'next_price' => 1,
        'next_invoice' => 1,
        'next_invoice_item' => 1,
        'customers' => [],
        'prices' => [],
        'products' => [],
        'invoice_items' => [],
        'invoices' => [],
    ];

    public static function resetStore(): void
    {
        self::writeStore(self::DEFAULT_STATE);
    }

    public static function setInvoiceState(string $invoiceId, array $attributes): void
    {
        $store = self::readStore();
        $invoice = $store['invoices'][$invoiceId] ?? null;
        if (!$invoice) {
            return;
        }

        $store['invoices'][$invoiceId] = [
            ...$invoice,
            ...$attributes,
        ];
        self::writeStore($store);
    }

    public function request($method, $absUrl, $headers, $params, $hasFile, $apiMode = 'v1')
    {
        $path = (string)parse_url((string)$absUrl, PHP_URL_PATH);
        $store = self::readStore();

        if ($method === 'post' && $path === '/v1/customers') {
            $customerId = sprintf('cus_fake_%06d', (int)$store['next_customer']);
            $store['next_customer']++;
            $customer = [
                'id' => $customerId,
                'object' => 'customer',
                'email' => (string)($params['email'] ?? ''),
            ];
            $store['customers'][$customerId] = $customer;
            self::writeStore($store);

            return $this->jsonResponse($customer);
        }

        if ($method === 'get' && preg_match('#^/v1/customers/(?P<id>[^/]+)$#', $path, $matches)) {
            $customer = $store['customers'][$matches['id']] ?? null;
            if (!$customer) {
                return $this->errorResponse(404, 'resource_missing', 'No such customer.');
            }

            return $this->jsonResponse($customer);
        }

        if ($method === 'get' && preg_match('#^/v1/products/(?P<id>[^/]+)$#', $path, $matches)) {
            $product = $store['products'][$matches['id']] ?? null;
            if (!$product) {
                return $this->errorResponse(404, 'resource_missing', 'No such product.');
            }

            return $this->jsonResponse($product);
        }

        if ($method === 'post' && $path === '/v1/products') {
            $productId = (string)($params['id'] ?? sprintf('prod_fake_%06d', count($store['products']) + 1));
            $product = [
                'id' => $productId,
                'object' => 'product',
                'name' => (string)($params['name'] ?? 'Fake Product'),
            ];
            $store['products'][$productId] = $product;
            self::writeStore($store);

            return $this->jsonResponse($product);
        }

        if ($method === 'post' && $path === '/v1/prices') {
            $priceId = sprintf('price_fake_%06d', (int)$store['next_price']);
            $store['next_price']++;
            $price = [
                'id' => $priceId,
                'object' => 'price',
                'product' => (string)($params['product'] ?? ''),
                'unit_amount' => (int)($params['unit_amount'] ?? 0),
                'currency' => strtolower((string)($params['currency'] ?? 'dkk')),
            ];
            $store['prices'][$priceId] = $price;
            self::writeStore($store);

            return $this->jsonResponse($price);
        }

        if ($method === 'post' && $path === '/v1/invoices') {
            $invoiceId = sprintf('in_fake_%06d', (int)$store['next_invoice']);
            $store['next_invoice']++;
            $invoice = [
                'id' => $invoiceId,
                'object' => 'invoice',
                'customer' => (string)($params['customer'] ?? ''),
                'status' => 'draft',
                'paid' => false,
                'amount_due' => 0,
                'amount_paid' => 0,
                'collection_method' => (string)($params['collection_method'] ?? 'send_invoice'),
                'hosted_invoice_url' => sprintf('https://stripe.test/invoices/%s', $invoiceId),
                'metadata' => is_array($params['metadata'] ?? null) ? $params['metadata'] : [],
                'lines' => [],
            ];
            $store['invoices'][$invoiceId] = $invoice;
            self::writeStore($store);

            return $this->jsonResponse($invoice);
        }

        if ($method === 'post' && $path === '/v1/invoiceitems') {
            $invoiceId = (string)($params['invoice'] ?? '');
            $invoice = $store['invoices'][$invoiceId] ?? null;
            if (!$invoice) {
                return $this->errorResponse(404, 'resource_missing', 'No such invoice.');
            }

            $invoiceItemId = sprintf('ii_fake_%06d', (int)$store['next_invoice_item']);
            $store['next_invoice_item']++;
            $priceId = (string)($params['price'] ?? '');
            $price = $store['prices'][$priceId] ?? ['unit_amount' => 0];
            $invoiceItem = [
                'id' => $invoiceItemId,
                'object' => 'invoiceitem',
                'invoice' => $invoiceId,
                'customer' => (string)($params['customer'] ?? ''),
                'price' => $priceId,
                'amount' => (int)($price['unit_amount'] ?? 0),
            ];

            $store['invoice_items'][$invoiceItemId] = $invoiceItem;
            $store['invoices'][$invoiceId]['lines'][] = $invoiceItemId;
            $store['invoices'][$invoiceId]['amount_due'] += (int)($price['unit_amount'] ?? 0);
            self::writeStore($store);

            return $this->jsonResponse($invoiceItem);
        }

        if ($method === 'post' && preg_match('#^/v1/invoices/(?P<id>[^/]+)/finalize$#', $path, $matches)) {
            $invoiceId = $matches['id'];
            $invoice = $store['invoices'][$invoiceId] ?? null;
            if (!$invoice) {
                return $this->errorResponse(404, 'resource_missing', 'No such invoice.');
            }

            $invoice['status'] = 'open';
            $store['invoices'][$invoiceId] = $invoice;
            self::writeStore($store);

            return $this->jsonResponse($invoice);
        }

        if ($method === 'post' && preg_match('#^/v1/invoices/(?P<id>[^/]+)/void$#', $path, $matches)) {
            $invoiceId = $matches['id'];
            $invoice = $store['invoices'][$invoiceId] ?? null;
            if (!$invoice) {
                return $this->errorResponse(404, 'resource_missing', 'No such invoice.');
            }

            $invoice['status'] = 'void';
            $invoice['paid'] = false;
            $store['invoices'][$invoiceId] = $invoice;
            self::writeStore($store);

            return $this->jsonResponse($invoice);
        }

        if ($method === 'get' && preg_match('#^/v1/invoices/(?P<id>[^/]+)$#', $path, $matches)) {
            $invoice = $store['invoices'][$matches['id']] ?? null;
            if (!$invoice) {
                return $this->errorResponse(404, 'resource_missing', 'No such invoice.');
            }

            return $this->jsonResponse($invoice);
        }

        return $this->errorResponse(404, 'resource_missing', 'Unsupported fake Stripe request: ' . $method . ' ' . $path);
    }

    private function jsonResponse(array $payload, int $status = 200): array
    {
        return [
            json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            $status,
            [
                'Content-Type' => 'application/json',
                'Request-Id' => 'req_fake_stripe',
            ],
        ];
    }

    private function errorResponse(int $status, string $code, string $message): array
    {
        return [
            json_encode([
                'error' => [
                    'type' => 'invalid_request_error',
                    'code' => $code,
                    'message' => $message,
                ],
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            $status,
            [
                'Content-Type' => 'application/json',
                'Request-Id' => 'req_fake_stripe_error',
            ],
        ];
    }

    private static function readStore(): array
    {
        $path = self::storePath();
        if (!is_file($path)) {
            return self::DEFAULT_STATE;
        }

        $json = file_get_contents($path);
        if ($json === false || trim($json) === '') {
            return self::DEFAULT_STATE;
        }

        $decoded = json_decode($json, true);
        if (!is_array($decoded)) {
            return self::DEFAULT_STATE;
        }

        return [
            ...self::DEFAULT_STATE,
            ...$decoded,
        ];
    }

    private static function writeStore(array $store): void
    {
        file_put_contents(
            self::storePath(),
            json_encode($store, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
        );
    }

    private static function storePath(): string
    {
        $path = trim((string)getenv('STRIPE_FAKE_STORE_PATH'));
        if ($path !== '') {
            return $path;
        }

        return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'truckwash-stripe-fake-store.json';
    }
}
