<?php
declare(strict_types=1);

namespace App\Models;

class Payment extends BaseModel
{
    protected string $table = 'payments';

    public function createPending(array $data): array
    {
        $statement = $this->db()->prepare(
            'INSERT INTO payments
                (order_id, provider, provider_reference, payment_session_id, amount, currency, status,
                 card_type, card_brand, installment_count, is_3d_secure, raw_request, raw_response, created_at, updated_at)
             VALUES
                (:order_id, :provider, NULL, NULL, :amount, :currency, "pending",
                 :card_type, :card_brand, :installment_count, :is_3d_secure, :raw_request, NULL, NOW(), NOW())'
        );
        $statement->execute([
            'order_id' => (int) $data['order_id'],
            'provider' => $data['provider'],
            'amount' => $data['amount'],
            'currency' => $data['currency'],
            'card_type' => $data['card_type'] ?? null,
            'card_brand' => $data['card_brand'] ?? null,
            'installment_count' => (int) ($data['installment_count'] ?? 1),
            'is_3d_secure' => !empty($data['is_3d_secure']) ? 1 : 0,
            'raw_request' => json_encode($data['raw_request'] ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);

        return $this->find((int) $this->db()->lastInsertId()) ?? [];
    }

    public function updateSession(int $paymentId, array $providerResult): void
    {
        $statement = $this->db()->prepare(
            'UPDATE payments
             SET provider_reference = NULLIF(:provider_reference, ""),
                 payment_session_id = NULLIF(:payment_session_id, ""),
                 provider_payment_id = NULLIF(:provider_payment_id, ""),
                 card_brand = COALESCE(NULLIF(:card_brand, ""), card_brand),
                 raw_response = :raw_response,
                 updated_at = NOW()
             WHERE id = :id'
        );
        $statement->execute([
            'id' => $paymentId,
            'provider_reference' => (string) ($providerResult['provider_reference'] ?? ''),
            'payment_session_id' => (string) ($providerResult['payment_session_id'] ?? ''),
            'provider_payment_id' => (string) ($providerResult['provider_reference'] ?? ''),
            'card_brand' => (string) ($providerResult['card_brand'] ?? ''),
            'raw_response' => json_encode($this->sanitize($providerResult), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
    }

    public function markPaid(int $paymentId, string $providerReference): void
    {
        $statement = $this->db()->prepare(
            'UPDATE payments
             SET status = "paid",
                 provider_reference = NULLIF(:provider_reference, ""),
                 provider_payment_id = NULLIF(:provider_payment_id, ""),
                 paid_at = NOW(),
                 failed_at = NULL,
                 updated_at = NOW()
             WHERE id = :id'
        );
        $statement->execute([
            'id' => $paymentId,
            'provider_reference' => $providerReference,
            'provider_payment_id' => $providerReference,
        ]);
    }

    public function markFailed(int $paymentId, string $providerReference = ''): void
    {
        $statement = $this->db()->prepare(
            'UPDATE payments
             SET status = "failed",
                 provider_reference = NULLIF(:provider_reference, ""),
                 provider_payment_id = NULLIF(:provider_payment_id, ""),
                 failed_at = NOW(),
                 updated_at = NOW()
             WHERE id = :id'
        );
        $statement->execute([
            'id' => $paymentId,
            'provider_reference' => $providerReference,
            'provider_payment_id' => $providerReference,
        ]);
    }

    public function markPending(int $paymentId, string $providerReference = ''): void
    {
        $statement = $this->db()->prepare(
            'UPDATE payments
             SET status = "pending",
                 provider_reference = NULLIF(:provider_reference, ""),
                 provider_payment_id = NULLIF(:provider_payment_id, ""),
                 updated_at = NOW()
             WHERE id = :id'
        );
        $statement->execute([
            'id' => $paymentId,
            'provider_reference' => $providerReference,
            'provider_payment_id' => $providerReference,
        ]);
    }

    public function latestForOrder(int $orderId): ?array
    {
        $statement = $this->db()->prepare('SELECT * FROM payments WHERE order_id = :order_id ORDER BY id DESC LIMIT 1');
        $statement->execute(['order_id' => $orderId]);
        $row = $statement->fetch();

        return $row ?: null;
    }

    private function sanitize(array $payload): array
    {
        foreach (['card_number', 'cvv', 'cvc', 'expiry', 'exp_month', 'exp_year', 'card_holder', 'callback_payload'] as $key) {
            unset($payload[$key]);
        }

        return $payload;
    }
}
