<?php

namespace App\Libraries;

use CodeIgniter\Config\Services;

/**
 * Simplified client for Paytota API (gate.paytota.com).
 * Handles purchases (simple checkout), balance, payouts (initiate + execute), and status checks.
 */
class PaytotaService
{
    private $baseUrl;
    private $secretKey;
    private $brandId;
    private $defaultSuccessRedirect;
    private $defaultFailureRedirect;

    public function __construct()
    {
        $this->baseUrl = rtrim($_ENV['paytota.base_url'] ?? 'https://gate.paytota.com', '/');
        $this->secretKey = $_ENV['paytota.secret_key'] ?? '';
        $this->brandId = $_ENV['paytota.brand_id'] ?? '';
        $this->defaultSuccessRedirect = $_ENV['paytota.success_redirect'] ?? '';
        $this->defaultFailureRedirect = $_ENV['paytota.failure_redirect'] ?? '';
    }

    /**
     * Build common request headers for Paytota API.
     */
    private function getHeaders(): array
    {
        return [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $this->secretKey,
        ];
    }

    /**
     * Make a request and return normalized response.
     */
    private function request(string $method, string $url, array $options = []): array
    {
        $client = Services::curlrequest();
        $defaults = [
            'headers' => $this->getHeaders(),
            'timeout' => 30,
            'http_errors' => false,
        ];
        $options = array_merge($defaults, $options);
        $options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []);

        try {
            $response = $client->request($method, $url, $options);
            $body = $response->getBody();
            $data = json_decode($body, true) ?? [];
            $statusCode = $response->getStatusCode();

            if ($statusCode >= 200 && $statusCode < 300) {
                return [
                    'success' => true,
                    'message' => 'OK',
                    'data' => $data,
                ];
            }

            $message = $data['message'] ?? $data['error']['message'] ?? $data['detail'] ?? 'Request failed';
            if (is_array($message)) {
                $message = json_encode($message);
            }
            return [
                'success' => false,
                'message' => $message,
                'data' => $data,
                'status_code' => $statusCode,
            ];
        } catch (\Throwable $e) {
            log_message('error', 'Paytota API request failed: ' . $e->getMessage());
            return [
                'success' => false,
                'message' => $e->getMessage(),
                'data' => [],
            ];
        }
    }

    /**
     * Create a purchase (simple checkout). Returns checkout_url for redirect.
     * POST /api/v1/purchases/
     */
    public function createPurchase(array $data): array
    {
        $products = $data['products'] ?? [];
        if (empty($products)) {
            return ['success' => false, 'message' => 'At least one product is required', 'data' => []];
        }
        $productsPayload = [];
        foreach ($products as $p) {
            $productsPayload[] = [
                'name' => $p['name'] ?? 'Item',
                'price' => (string) ($p['price'] ?? '0'),
            ];
        }

        $successRedirect = $data['success_redirect'] ?? $this->defaultSuccessRedirect;
        $failureRedirect = $data['failure_redirect'] ?? $this->defaultFailureRedirect;
        if (empty($successRedirect) || empty($failureRedirect)) {
            return ['success' => false, 'message' => 'success_redirect and failure_redirect are required', 'data' => []];
        }

        $payload = [
            'client' => [
                'email' => $data['email'] ?? '',
                'phone' => $data['phone'] ?? '',
                'country' => $data['country'] ?? 'UG',
            ],
            'purchase' => [
                'currency' => $data['currency'] ?? 'UGX',
                'products' => $productsPayload,
            ],
            'reference' => $data['reference'] ?? bin2hex(random_bytes(8)),
            'brand_id' => $this->brandId,
            'success_redirect' => $successRedirect,
            'failure_redirect' => $failureRedirect,
        ];

        $result = $this->request('POST', $this->baseUrl . '/api/v1/purchases/', [
            'json' => $payload,
        ]);

        if (!$result['success']) {
            return $result;
        }

        $res = $result['data'];
        return [
            'success' => true,
            'message' => 'Purchase created',
            'data' => [
                'id' => $res['id'] ?? null,
                'checkout_url' => $res['checkout_url'] ?? null,
                'reference' => $res['reference'] ?? $payload['reference'],
                'status' => $res['status'] ?? null,
            ],
        ];
    }

    /**
     * Get purchase status. GET /api/v1/purchases/{id}/
     */
    public function getPurchaseStatus(string $id): array
    {
        $result = $this->request('GET', $this->baseUrl . '/api/v1/purchases/' . $id . '/');
        if (!$result['success']) {
            return $result;
        }
        $res = $result['data'];
        return [
            'success' => true,
            'message' => 'OK',
            'data' => [
                'id' => $res['id'] ?? null,
                'status' => $res['status'] ?? null,
                'reference' => $res['reference'] ?? null,
                'checkout_url' => $res['checkout_url'] ?? null,
                'payment' => $res['payment'] ?? null,
            ],
        ];
    }

    /**
     * Get account balance. GET /api/v1/account/json/balance/
     */
    public function getBalance(): array
    {
        $result = $this->request('GET', $this->baseUrl . '/api/v1/account/json/balance/');
        if (!$result['success']) {
            return $result;
        }
        return [
            'success' => true,
            'message' => 'OK',
            'data' => $result['data'],
        ];
    }

    /**
     * Create payout (Step 1). POST /api/v1/payouts/
     * Returns execution_url for Step 2.
     */
    public function createPayout(array $data): array
    {
        $payload = [
            'client' => [
                'email' => $data['email'] ?? '',
                'phone' => $data['phone'] ?? '',
                'country' => $data['country'] ?? 'UG',
            ],
            'payment' => [
                'currency' => $data['currency'] ?? 'UGX',
                'amount' => (string) ($data['amount'] ?? '0'),
                'description' => $data['description'] ?? 'Payout',
            ],
            'reference' => $data['reference'] ?? bin2hex(random_bytes(8)),
            'brand_id' => $this->brandId,
        ];
        if (!empty($data['bank_account'])) {
            $payload['client']['bank_account'] = $data['bank_account'];
        }

        $result = $this->request('POST', $this->baseUrl . '/api/v1/payouts/', [
            'json' => $payload,
        ]);

        if (!$result['success']) {
            return $result;
        }

        $res = $result['data'];
        return [
            'success' => true,
            'message' => 'Payout initiated',
            'data' => [
                'id' => $res['id'] ?? null,
                'execution_url' => $res['execution_url'] ?? null,
                'reference' => $res['reference'] ?? $payload['reference'],
                'status' => $res['status'] ?? null,
            ],
        ];
    }

    /**
     * Execute payout (Step 2). POST to execution_url with bank or mobile payload.
     * Payload must include payout_type: "bank" | "mobile" and corresponding fields.
     */
    public function executePayout(string $executionUrl, array $payload): array
    {
        if (empty($payload['payout_type'])) {
            return ['success' => false, 'message' => 'payout_type is required (bank or mobile)', 'data' => []];
        }
        $result = $this->request('POST', $executionUrl, [
            'json' => $payload,
        ]);
        if (!$result['success']) {
            return $result;
        }
        return [
            'success' => true,
            'message' => 'Payout execution submitted',
            'data' => $result['data'],
        ];
    }

    /**
     * Get payout status. GET /api/v1/payouts/{id}/
     */
    public function getPayoutStatus(string $id): array
    {
        $result = $this->request('GET', $this->baseUrl . '/api/v1/payouts/' . $id . '/');
        if (!$result['success']) {
            return $result;
        }
        $res = $result['data'];
        return [
            'success' => true,
            'message' => 'OK',
            'data' => [
                'id' => $res['id'] ?? null,
                'status' => $res['status'] ?? null,
                'reference' => $res['reference'] ?? null,
                'payment' => $res['payment'] ?? null,
            ],
        ];
    }

    /**
     * Execute STK push for a purchase (Step 2). POST {base}/p/{id}/ as form-data.
     */
    public function executeStk(string $purchaseId): array
    {
        $url = $this->baseUrl . '/p/' . $purchaseId . '/';
        $client = Services::curlrequest();
        try {
            $response = $client->post($url, [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->secretKey,
                ],
                'multipart' => [
                    ['name' => 's2s', 'contents' => 'true'],
                    ['name' => 'pm', 'contents' => 'paytota_proxy'],
                ],
                'timeout' => 30,
                'http_errors' => false,
            ]);
            $body = $response->getBody();
            $data = json_decode($body, true) ?? [];
            $statusCode = $response->getStatusCode();

            if ($statusCode >= 200 && $statusCode < 300) {
                return [
                    'success' => true,
                    'message' => $data['details']['message'] ?? 'STK sent',
                    'data' => $data,
                ];
            }
            return [
                'success' => false,
                'message' => $data['error']['message'] ?? $data['details']['message'] ?? 'STK execution failed',
                'data' => $data,
                'status_code' => $statusCode,
            ];
        } catch (\Throwable $e) {
            log_message('error', 'Paytota STK request failed: ' . $e->getMessage());
            return [
                'success' => false,
                'message' => $e->getMessage(),
                'data' => [],
            ];
        }
    }

    /**
     * Check if service is configured (has base URL and secret key).
     */
    public function isConfigured(): bool
    {
        return !empty($this->baseUrl) && !empty($this->secretKey);
    }
}
