<?php

namespace App\Services\Settings;

use App\Models\Business;
use App\Models\Setting;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;

class PaymentSettingsService
{
    public const GROUP = 'payment';

    public function defaults(Business $business): array
    {
        return [
            'general' => [
                'default_currency' => $business->currency ?: 'USD',
                'currency_position' => 'before',
                'enable_tax' => true,
                'tax_name' => 'Sales Tax',
                'tax_percentage' => 8,
                'invoice_due_days' => 7,
                'payment_success_message' => 'Thank you. Your payment has been recorded successfully.',
                'payment_failed_message' => 'We could not complete the payment. Please try again or contact the business.',
            ],
            'cash' => [
                'enabled' => true,
                'display_name' => 'Cash',
                'instructions' => 'Pay at the counter after your appointment.',
            ],
            'bank' => [
                'enabled' => true,
                'bank_name' => 'Booklio Bank',
                'account_holder_name' => $business->name,
                'account_number' => null,
                'swift_bic' => null,
                'branch_name' => 'Market Street Branch',
                'payment_instructions' => 'Please transfer the exact invoice amount and include the invoice number as your reference.',
                'upload_payment_proof' => true,
            ],
            'stripe' => [
                'enabled' => false,
                'mode' => 'test',
                'publishable_key' => null,
                'secret_key' => null,
                'webhook_secret' => null,
                'currency' => $business->currency ?: 'USD',
                'payment_description' => 'Booklio appointment invoice payment',
            ],
            'paypal' => [
                'enabled' => false,
                'mode' => 'sandbox',
                'client_id' => null,
                'client_secret' => null,
                'webhook_id' => null,
                'currency' => $business->currency ?: 'USD',
            ],
            'future_gateways' => [
                'paymob' => ['name' => 'PayMob', 'status' => 'coming_soon'],
                'razorpay' => ['name' => 'Razorpay', 'status' => 'coming_soon'],
                'mollie' => ['name' => 'Mollie', 'status' => 'coming_soon'],
                'paystack' => ['name' => 'Paystack', 'status' => 'coming_soon'],
                'flutterwave' => ['name' => 'Flutterwave', 'status' => 'coming_soon'],
            ],
        ];
    }

    public function get(Business $business, bool $revealSecrets = false): array
    {
        $settings = $this->defaults($business);
        $secretConfigured = [];

        Setting::query()
            ->forBusiness($business)
            ->where('group', self::GROUP)
            ->get()
            ->each(function (Setting $setting) use (&$settings, &$secretConfigured, $revealSecrets): void {
                $value = $this->storedValue($setting);
                $key = $this->normalizeSettingKey($setting->name);

                if ($setting->is_encrypted) {
                    data_set($secretConfigured, $key, filled($value));
                    $value = $revealSecrets ? $value : null;
                }

                data_set($settings, $key, $value);
            });

        $settings['_secret_configured'] = $secretConfigured;

        return $settings;
    }

    public function save(Business $business, array $payload): void
    {
        $defaults = $this->defaults($business);

        foreach ($this->flatten($payload) as $key => $value) {
            if (! $this->isKnownKey($defaults, $key)) {
                continue;
            }

            if ($this->isSecret($key)) {
                if (! filled($value)) {
                    continue;
                }

                $this->put($business, $key, Crypt::encryptString((string) $value), true);

                continue;
            }

            $this->put($business, $key, $value, false);
        }

        foreach ($defaults['future_gateways'] as $key => $gateway) {
            $this->put($business, 'future_gateways.'.$key, $gateway, false);
        }
    }

    public function testPlaceholder(string $gateway): string
    {
        $label = match ($gateway) {
            'stripe' => 'Stripe',
            'paypal' => 'PayPal',
            'cash' => 'Cash payment',
            'bank' => 'Bank transfer',
            default => ucfirst($gateway),
        };

        return "{$label} connection check is ready for backend API wiring. No external request was sent.";
    }

    public function secretConfigured(array $settings, string $key): bool
    {
        return (bool) data_get($settings, '_secret_configured.'.$key, false);
    }

    public function futureGateways(): array
    {
        return [
            ['key' => 'paymob', 'name' => 'PayMob', 'description' => 'Regional card and wallet gateway placeholder.'],
            ['key' => 'razorpay', 'name' => 'Razorpay', 'description' => 'India-focused card, UPI, and wallet gateway placeholder.'],
            ['key' => 'mollie', 'name' => 'Mollie', 'description' => 'European payment methods placeholder.'],
            ['key' => 'paystack', 'name' => 'Paystack', 'description' => 'Africa-focused card and bank payment placeholder.'],
            ['key' => 'flutterwave', 'name' => 'Flutterwave', 'description' => 'Pan-African payment gateway placeholder.'],
        ];
    }

    public static function secretKeys(): array
    {
        return [
            'bank.account_number',
            'bank.swift_bic',
            'stripe.publishable_key',
            'stripe.secret_key',
            'stripe.webhook_secret',
            'paypal.client_id',
            'paypal.client_secret',
            'paypal.webhook_id',
        ];
    }

    private function put(Business $business, string $name, mixed $value, bool $encrypted): void
    {
        Setting::query()->updateOrCreate(
            ['business_id' => $business->id, 'group' => self::GROUP, 'name' => $name],
            ['value' => ['value' => $value], 'is_encrypted' => $encrypted]
        );
    }

    private function storedValue(Setting $setting): mixed
    {
        $value = $setting->value;
        $value = is_array($value) && array_key_exists('value', $value) ? $value['value'] : $value;

        if (! $setting->is_encrypted || ! filled($value)) {
            return $value;
        }

        try {
            return Crypt::decryptString((string) $value);
        } catch (DecryptException) {
            return null;
        }
    }

    private function flatten(array $payload, string $prefix = ''): array
    {
        $items = [];

        foreach ($payload as $key => $value) {
            $path = $prefix === '' ? (string) $key : $prefix.'.'.$key;

            if (is_array($value)) {
                $items += $this->flatten($value, $path);
                continue;
            }

            $items[$path] = $value;
        }

        return $items;
    }

    private function isKnownKey(array $defaults, string $key): bool
    {
        return data_get($defaults, $key, '__missing__') !== '__missing__';
    }

    private function isSecret(string $key): bool
    {
        return in_array($key, self::secretKeys(), true);
    }

    private function normalizeSettingKey(string $key): string
    {
        return match ($key) {
            'default_currency' => 'general.default_currency',
            default => $key,
        };
    }
}
