<?php

namespace App\Services;

use App\Models\Association;
use App\Exceptions\PaystackException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Crypt;

/**
 * Multi-tenant Paystack Service
 *
 * Handles payment processing using association-specific Paystack credentials.
 * This ensures complete payment isolation - each association's payments go
 * directly to their own Paystack account, never touching Guild360.
 */
class AssociationPaystackService
{
    private string $secretKey;
    private string $publicKey;
    private ?string $webhookSecret;
    private Association $association;
    private string $baseUrl = 'https://api.paystack.co';

    public function __construct(Association $association)
    {
        $this->association = $association;
        $this->initializeCredentials();
    }

    /**
     * Initialize credentials based on association configuration
     */
    private function initializeCredentials(): void
    {
        // Check if association has configured their own Paystack keys
        if ($this->association->paystack_configured &&
            $this->association->paystack_secret_key) {

            // Use association's own keys (decrypted)
            $this->secretKey = Crypt::decryptString($this->association->paystack_secret_key);
            $this->publicKey = Crypt::decryptString($this->association->paystack_public_key);
            $this->webhookSecret = $this->association->paystack_webhook_secret
                ? Crypt::decryptString($this->association->paystack_webhook_secret)
                : null;

        } else {
            // No Paystack configured - throw exception
            throw new PaystackException(
                'Paystack not configured for this association. Please configure payment settings.',
                [],
                422
            );
        }
    }

    /**
     * Initialize a Paystack transaction
     *
     * @param  string  $email         Customer email
     * @param  int     $amountInKobo  Amount in kobo (NGN * 100)
     * @param  string  $reference     Unique transaction reference
     * @param  array   $metadata      Additional metadata
     * @param  string|null $callbackUrl  URL to redirect after payment
     * @return array{authorization_url: string, access_code: string, reference: string}
     *
     * @throws PaystackException
     */
    public function initializeTransaction(
        string $email,
        int $amountInKobo,
        string $reference,
        array $metadata = [],
        ?string $callbackUrl = null,
    ): array {
        $payload = [
            'email' => $email,
            'amount' => $amountInKobo,
            'reference' => $reference,
            'metadata' => array_merge($metadata, [
                'association_id' => $this->association->id,
                'association_name' => $this->association->name,
            ]),
            'currency' => 'NGN',
        ];

        if ($callbackUrl) {
            $payload['callback_url'] = $callbackUrl;
        }

        $response = Http::withToken($this->secretKey)
            ->timeout(30)
            ->post("{$this->baseUrl}/transaction/initialize", $payload);

        if ($response->failed() || !$response->json('status')) {
            Log::error('Paystack: Failed to initialize transaction', [
                'association_id' => $this->association->id,
                'reference' => $reference,
                'status_code' => $response->status(),
                'body' => $response->json(),
            ]);

            throw new PaystackException(
                $response->json('message', 'Failed to initialize Paystack transaction'),
                $response->json() ?? [],
                $response->status(),
            );
        }

        $data = $response->json('data');

        return [
            'authorization_url' => $data['authorization_url'],
            'access_code' => $data['access_code'],
            'reference' => $data['reference'],
        ];
    }

    /**
     * Verify a Paystack transaction by reference
     *
     * @param  string  $reference
     * @return array  Full transaction data from Paystack
     *
     * @throws PaystackException
     */
    public function verifyTransaction(string $reference): array
    {
        $response = Http::withToken($this->secretKey)
            ->timeout(30)
            ->get("{$this->baseUrl}/transaction/verify/{$reference}");

        if ($response->failed() || !$response->json('status')) {
            Log::error('Paystack: Failed to verify transaction', [
                'association_id' => $this->association->id,
                'reference' => $reference,
                'status_code' => $response->status(),
                'body' => $response->json(),
            ]);

            throw new PaystackException(
                $response->json('message', 'Failed to verify Paystack transaction'),
                $response->json() ?? [],
                $response->status(),
            );
        }

        return $response->json('data');
    }

    /**
     * Validate Paystack webhook signature
     *
     * @param  string  $payload    Raw request body
     * @param  string  $signature  X-Paystack-Signature header value
     * @return bool
     */
    public function validateWebhookSignature(string $payload, string $signature): bool
    {
        if (empty($this->webhookSecret)) {
            Log::warning('Paystack: Webhook secret not configured', [
                'association_id' => $this->association->id,
            ]);
            return false;
        }

        $computed = hash_hmac('sha512', $payload, $this->webhookSecret);

        return hash_equals($computed, $signature);
    }

    /**
     * Get the public key for this association
     *
     * @return string
     */
    public function getPublicKey(): string
    {
        return $this->publicKey;
    }

    /**
     * Get the association instance
     *
     * @return Association
     */
    public function getAssociation(): Association
    {
        return $this->association;
    }

    /**
     * Check if association has Paystack configured
     *
     * @param Association $association
     * @return bool
     */
    public static function isConfigured(Association $association): bool
    {
        return $association->paystack_configured &&
               !empty($association->paystack_secret_key) &&
               !empty($association->paystack_public_key);
    }
}
