<?php

namespace App\Services\Messaging;

use App\Models\MessagingCredential;
use App\Models\Organization;

class MessagingCredentialsService
{
    public function __construct(
        private CustomerSmsClient $sms,
        private CustomerEmailClient $email,
    ) {}

    public function forOrganization(Organization $organization): MessagingCredential
    {
        $existing = MessagingCredential::query()
            ->where('organization_id', $organization->id)
            ->first();
        if ($existing) {
            return $existing;
        }

        try {
            return MessagingCredential::query()->create([
                'organization_id' => $organization->id,
                'owner_ref' => $organization->owner_ref,
            ]);
        } catch (\Throwable $e) {
            // Concurrent create (or a long demo-seed transaction holding a lock)
            // — prefer returning the row another request just wrote over 500ing.
            $existing = MessagingCredential::query()
                ->where('organization_id', $organization->id)
                ->first();
            if ($existing) {
                return $existing;
            }

            throw $e;
        }
    }

    /**
     * @return array{ok: bool, error?: string, credential?: MessagingCredential, senders?: list<string>}
     */
    public function validateAndSaveSms(Organization $organization, string $apiKey, string $senderId): array
    {
        $apiKey = $this->sanitizeApiKey($apiKey);
        $senderId = trim($senderId);

        if (! str_starts_with($apiKey, 'lsk_sms_live_')) {
            return ['ok' => false, 'error' => 'SMS API keys must start with lsk_sms_live_.'];
        }

        if ($senderId === '' || strlen($senderId) > 11) {
            return ['ok' => false, 'error' => 'Sender ID must be 1–11 characters.'];
        }

        $me = $this->sms->me($apiKey);
        if (! ($me['ok'] ?? false)) {
            $credential = $this->forOrganization($organization);
            $credential->update([
                'sms_status' => MessagingCredential::STATUS_INVALID,
                'sms_last_error' => $me['error'] ?? 'Invalid SMS API key.',
            ]);

            return ['ok' => false, 'error' => $me['error'] ?? 'Invalid SMS API key.', 'credential' => $credential];
        }

        $senders = $this->sms->senders($apiKey);
        $approved = collect($senders['data']['data'] ?? [])->pluck('sender_id')->map(fn ($id) => (string) $id)->all();
        $defaultSender = (string) ($senders['data']['default_sender'] ?? '');

        $allowed = array_values(array_unique(array_filter([...$approved, $defaultSender])));
        if ($allowed !== [] && ! in_array($senderId, $allowed, true)) {
            $error = 'Sender ID is not approved for this SMS key. Approved: '.implode(', ', $allowed);

            $credential = $this->forOrganization($organization);
            $credential->update([
                'sms_status' => MessagingCredential::STATUS_INVALID,
                'sms_last_error' => $error,
            ]);

            return ['ok' => false, 'error' => $error, 'credential' => $credential, 'senders' => $allowed];
        }

        $credential = $this->forOrganization($organization);
        $credential->update([
            'owner_ref' => $organization->owner_ref,
            'sms_api_key_encrypted' => MessagingCredential::encryptKey($apiKey),
            'sms_api_key_prefix' => substr($apiKey, 0, 16),
            'sms_sender_id' => $senderId,
            'sms_status' => MessagingCredential::STATUS_VALID,
            'sms_validated_at' => now(),
            'sms_last_error' => null,
        ]);

        return ['ok' => true, 'credential' => $credential->fresh(), 'senders' => $allowed];
    }

    /**
     * @return array{ok: bool, error?: string, credential?: MessagingCredential}
     */
    public function validateAndSaveBird(Organization $organization, string $apiKey, string $fromEmail, ?string $fromName): array
    {
        $apiKey = $this->sanitizeApiKey($apiKey);
        $fromEmail = strtolower(trim($fromEmail));
        $fromName = trim((string) $fromName);

        // Platform SMTP relay only accepts HTTP API keys issued as lsk_live_…
        if (! str_starts_with($apiKey, 'lsk_live_')) {
            return ['ok' => false, 'error' => 'Bird API keys must start with lsk_live_. Copy the HTTP API key from bird.ladill.com (not the mailbox password).'];
        }

        if (! filter_var($fromEmail, FILTER_VALIDATE_EMAIL)) {
            return ['ok' => false, 'error' => 'Enter a valid from email address.'];
        }

        $me = $this->email->me($apiKey);
        if (! ($me['ok'] ?? false)) {
            $error = $me['error'] ?? 'Invalid Bird API key.';
            $credential = $this->forOrganization($organization);
            $credential->update([
                'bird_status' => MessagingCredential::STATUS_INVALID,
                'bird_last_error' => $error,
            ]);

            return ['ok' => false, 'error' => $error, 'credential' => $credential];
        }

        $domains = collect($me['data']['verified_domains'] ?? [])->map(fn ($d) => strtolower((string) $d))->all();
        $fromDomain = strtolower((string) (explode('@', $fromEmail, 2)[1] ?? ''));
        if ($domains !== [] && ! in_array($fromDomain, $domains, true)) {
            $error = 'From email must use a verified Bird domain ('.implode(', ', $domains).').';
            $credential = $this->forOrganization($organization);
            $credential->update([
                'bird_status' => MessagingCredential::STATUS_INVALID,
                'bird_last_error' => $error,
            ]);

            return ['ok' => false, 'error' => $error, 'credential' => $credential];
        }

        $credential = $this->forOrganization($organization);
        $credential->update([
            'owner_ref' => $organization->owner_ref,
            'bird_api_key_encrypted' => MessagingCredential::encryptKey($apiKey),
            'bird_api_key_prefix' => substr($apiKey, 0, 16),
            'bird_from_email' => $fromEmail,
            'bird_from_name' => $fromName !== '' ? $fromName : null,
            'bird_status' => MessagingCredential::STATUS_VALID,
            'bird_validated_at' => now(),
            'bird_last_error' => null,
        ]);

        return ['ok' => true, 'credential' => $credential->fresh()];
    }

    public function disconnectSms(Organization $organization): MessagingCredential
    {
        $credential = $this->forOrganization($organization);
        $credential->update([
            'sms_api_key_encrypted' => null,
            'sms_api_key_prefix' => null,
            'sms_sender_id' => null,
            'sms_status' => null,
            'sms_validated_at' => null,
            'sms_last_error' => null,
        ]);

        return $credential->fresh();
    }

    public function disconnectBird(Organization $organization): MessagingCredential
    {
        $credential = $this->forOrganization($organization);
        $credential->update([
            'bird_api_key_encrypted' => null,
            'bird_api_key_prefix' => null,
            'bird_from_email' => null,
            'bird_from_name' => null,
            'bird_status' => null,
            'bird_validated_at' => null,
            'bird_last_error' => null,
        ]);

        return $credential->fresh();
    }

    private function sanitizeApiKey(string $apiKey): string
    {
        $apiKey = trim($apiKey);
        // Strip zero-width / BOM / whitespace that often sneaks in on paste.
        $cleaned = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}\s]+/u', '', $apiKey);

        return is_string($cleaned) ? $cleaned : $apiKey;
    }
}
