<?php

namespace App\Services;

use App\Models\CommunicationUnitTransaction;
use App\Models\Customer;
use App\Models\Tenant;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;

class WhatsappService
{
    public function __construct(
        private readonly PlatformSettings $settings,
        private readonly CommunicationUnitService $units,
    ) {
    }

    public function send(string $phone, string $message, ?Tenant $tenant = null, ?Customer $customer = null, array $context = []): array
    {
        $reservation = $this->reserveUnits($tenant, $customer, $message, $context);

        if (! $reservation['successful']) {
            return [
                'status' => 'failed',
                'message' => $reservation['message'],
                'units' => $reservation['units'],
            ];
        }

        $settings = $this->settings->whatsappSettings($tenant);
        $driver = $settings['driver'];
        $url = $settings['url'];

        if ($driver === 'ebulksms') {
            return $this->sendViaEbulkSms($phone, $message, $settings, $reservation);
        }

        if ($driver === 'log' || blank($url)) {
            Log::info('WhatsApp notification logged.', [
                'tenant_id' => $tenant?->id,
                'to' => $phone,
                'message' => $message,
            ]);

            return [
                'status' => 'logged',
                'message' => 'WhatsApp gateway is not configured, so the message was written to the log.',
                'units' => $reservation['units'],
            ];
        }

        try {
            $payload = [
                $settings['to_param'] => $phone,
                $settings['message_param'] => $message,
            ];

            if (filled($settings['sender'])) {
                $payload[$settings['sender_param']] = $settings['sender'];
            }

            $request = Http::timeout(20)->acceptJson();

            if (filled($settings['token'])) {
                $request = $request->withToken($settings['token']);
            }

            $response = strtoupper($settings['method']) === 'GET'
                ? $request->get($url, $payload)
                : $request->post($url, $payload);

            if ($response->successful()) {
                return ['status' => 'sent', 'message' => 'WhatsApp message sent.', 'units' => $reservation['units']];
            }

            $this->refundUnits($reservation['transaction'], 'WhatsApp gateway returned HTTP '.$response->status().'.');

            return [
                'status' => 'failed',
                'message' => 'WhatsApp gateway returned HTTP '.$response->status().'.',
                'units' => $reservation['units'],
            ];
        } catch (Throwable $exception) {
            report($exception);
            $this->refundUnits($reservation['transaction'], $exception->getMessage());

            return [
                'status' => 'failed',
                'message' => $exception->getMessage(),
                'units' => $reservation['units'],
            ];
        }
    }

    /**
     * @return array{successful: bool, message: string, units: int, transaction: CommunicationUnitTransaction|null}
     */
    private function reserveUnits(?Tenant $tenant, ?Customer $customer, string $message, array $context): array
    {
        if (! $tenant) {
            return ['successful' => true, 'message' => 'Platform message.', 'units' => 0, 'transaction' => null];
        }

        $context['message_length'] = function_exists('mb_strlen') ? mb_strlen($message) : strlen($message);

        return $this->units->reserveForSend($tenant, CommunicationUnitService::CHANNEL_WHATSAPP, $message, $customer, $context);
    }

    private function refundUnits(?CommunicationUnitTransaction $transaction, string $reason): void
    {
        if ($transaction) {
            $this->units->refundUsage($transaction, $reason);
        }
    }

    private function sendViaEbulkSms(string $phone, string $message, array $settings, array $reservation): array
    {
        $url = filled($settings['url'] ?? null)
            ? $settings['url']
            : 'https://api.ebulksms.com/sendwhatsapp.json';
        $username = $settings['username'] ?? null;
        $apiKey = $settings['token'] ?? null;
        $subject = $settings['sender'] ?? null;

        if (blank($username) || blank($apiKey) || blank($subject)) {
            $this->refundUnits($reservation['transaction'], 'eBulkSMS WhatsApp username, API key, or subject is missing.');

            return [
                'status' => 'failed',
                'message' => 'eBulkSMS WhatsApp username, API key, and subject are required.',
                'units' => $reservation['units'],
            ];
        }

        try {
            $response = Http::timeout(20)
                ->acceptJson()
                ->asJson()
                ->post($url, [
                    'WA' => [
                        'auth' => [
                            'username' => $username,
                            'apikey' => $apiKey,
                        ],
                        'message' => [
                            'subject' => $subject,
                            'messagetext' => $message,
                        ],
                        'recipients' => [$phone],
                    ],
                ]);

            $status = strtoupper((string) ($response->json('response.status') ?? $response->json('status') ?? ''));

            if ($response->successful() && $status === 'SUCCESS') {
                return [
                    'status' => 'sent',
                    'message' => 'WhatsApp message sent through eBulkSMS.',
                    'units' => $reservation['units'],
                ];
            }

            $error = $status !== '' ? $status : (trim($response->body()) ?: 'eBulkSMS WhatsApp returned HTTP '.$response->status().'.');
            $this->refundUnits($reservation['transaction'], $error);

            return [
                'status' => 'failed',
                'message' => 'eBulkSMS WhatsApp failed: '.$error,
                'units' => $reservation['units'],
            ];
        } catch (Throwable $exception) {
            report($exception);
            $this->refundUnits($reservation['transaction'], $exception->getMessage());

            return [
                'status' => 'failed',
                'message' => $exception->getMessage(),
                'units' => $reservation['units'],
            ];
        }
    }
}
