<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Jobs\SendAdminAnalyticsReportJob;
use App\Services\AuditLogService;
use App\Services\SystemSettingService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;

class IntegrationSettingController extends Controller
{
    public function sendTestReport(Request $request, AuditLogService $auditLogService): RedirectResponse
    {
        $data = $request->validate([
            'frequency' => ['required', 'in:daily,weekly'],
        ]);

        SendAdminAnalyticsReportJob::dispatchSync((string) $data['frequency']);

        $auditLogService->log(
            admin: $request->user(),
            action: 'integration_settings.test_report_sent',
            entity: 'system_settings',
            entityId: 'admin.reports',
            oldValues: null,
            newValues: ['frequency' => (string) $data['frequency']],
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', ucfirst((string) $data['frequency']).' test report dispatched.');
    }

    public function edit(SystemSettingService $systemSettingService): Response
    {
        $monnify = $systemSettingService->many('payments.monnify', [
            'base_url' => config('services.monnify.base_url'),
            'contract_code' => config('services.monnify.contract_code'),
            'timeout' => (int) config('services.monnify.timeout', 30),
        ]);
        $paystack = $systemSettingService->many('payments.paystack', [
            'base_url' => config('services.paystack.base_url', 'https://api.paystack.co'),
            'timeout' => (int) config('services.paystack.timeout', 30),
        ]);
        $paymentPoint = $systemSettingService->many('payments.paymentpoint', [
            'base_url' => config('services.paymentpoint.base_url', 'https://api.paymentpoint.co'),
            'timeout' => (int) config('services.paymentpoint.timeout', 30),
            'business_id' => config('services.paymentpoint.business_id', ''),
            'bank_codes' => config('services.paymentpoint.bank_codes', ['20946', '20897']),
        ]);
        $methodToggles = $systemSettingService->many('payments.methods', [
            'monnify_reserved_account_enabled' => true,
            'paymentpoint_reserved_account_enabled' => false,
            'monnify_instant_checkout_enabled' => false,
            'paystack_checkout_enabled' => false,
            'require_bvn_for_monnify' => false,
        ]);
        $adminReports = $systemSettingService->many('admin.reports', [
            'daily_enabled' => false,
            'weekly_enabled' => false,
            'recipients_csv' => '',
        ]);

        $webhookSettings = [
            'monnify_signature_header' => $systemSettingService->get(
                'webhooks.monnify',
                'signature_header',
                config('vtu.webhooks.monnify.signature_header', 'monnify-signature')
            ),
            'has_monnify_secret' => $systemSettingService->has('webhooks.monnify', 'secret')
                || filled(config('vtu.webhooks.monnify.secret')),
            'has_smeplug_secret' => $systemSettingService->has('webhooks.smeplug', 'secret')
                || filled(config('vtu.webhooks.smeplug.secret')),
            'has_paystack_secret' => $systemSettingService->has('webhooks.paystack', 'secret')
                || filled(config('vtu.webhooks.paystack.secret')),
            'paymentpoint_signature_header' => $systemSettingService->get(
                'webhooks.paymentpoint',
                'signature_header',
                config('vtu.webhooks.paymentpoint.signature_header', 'Paymentpoint-Signature')
            ),
            'has_paymentpoint_secret' => $systemSettingService->has('webhooks.paymentpoint', 'secret')
                || filled(config('vtu.webhooks.paymentpoint.secret')),
        ];

        return Inertia::render('Admin/Settings/Integrations', [
            'settings' => [
                'monnify_base_url' => (string) ($monnify['base_url'] ?? ''),
                'monnify_contract_code' => (string) ($monnify['contract_code'] ?? ''),
                'monnify_timeout' => (int) ($monnify['timeout'] ?? 30),
                'has_monnify_api_key' => $systemSettingService->has('payments.monnify', 'api_key')
                    || filled(config('services.monnify.api_key')),
                'has_monnify_secret_key' => $systemSettingService->has('payments.monnify', 'secret_key')
                    || filled(config('services.monnify.secret_key')),
                'paystack_base_url' => (string) ($paystack['base_url'] ?? ''),
                'paystack_timeout' => (int) ($paystack['timeout'] ?? 30),
                'has_paystack_public_key' => $systemSettingService->has('payments.paystack', 'public_key')
                    || filled(config('services.paystack.public_key')),
                'has_paystack_secret_key' => $systemSettingService->has('payments.paystack', 'secret_key')
                    || filled(config('services.paystack.secret_key')),
                'paymentpoint_base_url' => (string) ($paymentPoint['base_url'] ?? ''),
                'paymentpoint_timeout' => (int) ($paymentPoint['timeout'] ?? 30),
                'paymentpoint_business_id' => (string) ($paymentPoint['business_id'] ?? ''),
                'paymentpoint_bank_codes_csv' => implode(',', array_values((array) ($paymentPoint['bank_codes'] ?? []))),
                'has_paymentpoint_api_key' => $systemSettingService->has('payments.paymentpoint', 'api_key')
                    || filled(config('services.paymentpoint.api_key')),
                'has_paymentpoint_secret_key' => $systemSettingService->has('payments.paymentpoint', 'secret_key')
                    || filled(config('services.paymentpoint.secret_key')),
                'monnify_reserved_account_enabled' => (bool) ($methodToggles['monnify_reserved_account_enabled'] ?? true),
                'paymentpoint_reserved_account_enabled' => (bool) ($methodToggles['paymentpoint_reserved_account_enabled'] ?? false),
                'monnify_instant_checkout_enabled' => (bool) ($methodToggles['monnify_instant_checkout_enabled'] ?? false),
                'paystack_checkout_enabled' => (bool) ($methodToggles['paystack_checkout_enabled'] ?? false),
                'require_bvn_for_monnify' => (bool) ($methodToggles['require_bvn_for_monnify'] ?? false),
                'admin_report_daily_enabled' => (bool) ($adminReports['daily_enabled'] ?? false),
                'admin_report_weekly_enabled' => (bool) ($adminReports['weekly_enabled'] ?? false),
                'admin_report_recipients' => (string) ($adminReports['recipients_csv'] ?? ''),
                ...$webhookSettings,
            ],
        ]);
    }

    public function update(
        Request $request,
        SystemSettingService $systemSettingService,
        AuditLogService $auditLogService
    ): RedirectResponse {
        $data = $request->validate([
            'monnify_base_url' => ['nullable', 'string', 'max:2048', function ($attribute, $value, $fail) {
                if (blank($value)) {
                    return;
                }

                $this->validateHttpsUrl((string) $value, $attribute, $fail);
            }],
            'monnify_contract_code' => ['nullable', 'string', 'max:100'],
            'monnify_timeout' => ['nullable', 'integer', 'min:5', 'max:120'],
            'monnify_api_key' => ['nullable', 'string', 'max:255'],
            'monnify_secret_key' => ['nullable', 'string', 'max:255'],
            'paystack_base_url' => ['nullable', 'string', 'max:2048', function ($attribute, $value, $fail) {
                if (blank($value)) {
                    return;
                }

                $this->validateHttpsUrl((string) $value, $attribute, $fail);
            }],
            'paystack_timeout' => ['nullable', 'integer', 'min:5', 'max:120'],
            'paystack_public_key' => ['nullable', 'string', 'max:255'],
            'paystack_secret_key' => ['nullable', 'string', 'max:255'],
            'paymentpoint_base_url' => ['nullable', 'string', 'max:2048', function ($attribute, $value, $fail) {
                if (blank($value)) {
                    return;
                }

                $this->validateHttpsUrl((string) $value, $attribute, $fail);
            }],
            'paymentpoint_timeout' => ['nullable', 'integer', 'min:5', 'max:120'],
            'paymentpoint_api_key' => ['nullable', 'string', 'max:255'],
            'paymentpoint_secret_key' => ['nullable', 'string', 'max:255'],
            'paymentpoint_business_id' => ['nullable', 'string', 'max:255'],
            'paymentpoint_bank_codes_csv' => ['nullable', 'string', 'max:1000'],
            'monnify_webhook_secret' => ['nullable', 'string', 'max:255'],
            'monnify_signature_header' => ['nullable', 'string', 'max:100'],
            'smeplug_webhook_secret' => ['nullable', 'string', 'max:255'],
            'paystack_webhook_secret' => ['nullable', 'string', 'max:255'],
            'paymentpoint_webhook_secret' => ['nullable', 'string', 'max:255'],
            'paymentpoint_signature_header' => ['nullable', 'string', 'max:100'],
            'monnify_reserved_account_enabled' => ['nullable', 'boolean'],
            'paymentpoint_reserved_account_enabled' => ['nullable', 'boolean'],
            'monnify_instant_checkout_enabled' => ['nullable', 'boolean'],
            'paystack_checkout_enabled' => ['nullable', 'boolean'],
            'require_bvn_for_monnify' => ['nullable', 'boolean'],
            'admin_report_daily_enabled' => ['nullable', 'boolean'],
            'admin_report_weekly_enabled' => ['nullable', 'boolean'],
            'admin_report_recipients' => ['nullable', 'string', 'max:5000'],
            'clear_monnify_api_key' => ['nullable', 'boolean'],
            'clear_monnify_secret_key' => ['nullable', 'boolean'],
            'clear_paystack_public_key' => ['nullable', 'boolean'],
            'clear_paystack_secret_key' => ['nullable', 'boolean'],
            'clear_paymentpoint_api_key' => ['nullable', 'boolean'],
            'clear_paymentpoint_secret_key' => ['nullable', 'boolean'],
            'clear_monnify_webhook_secret' => ['nullable', 'boolean'],
            'clear_smeplug_webhook_secret' => ['nullable', 'boolean'],
            'clear_paystack_webhook_secret' => ['nullable', 'boolean'],
            'clear_paymentpoint_webhook_secret' => ['nullable', 'boolean'],
        ]);

        $old = $this->snapshotForAudit($systemSettingService);

        $entries = [
            [
                'section' => 'payments.monnify',
                'key' => 'base_url',
                'value' => $data['monnify_base_url'] ?? null,
                'type' => 'string',
                'clear' => blank($data['monnify_base_url'] ?? null),
            ],
            [
                'section' => 'payments.monnify',
                'key' => 'contract_code',
                'value' => $data['monnify_contract_code'] ?? null,
                'type' => 'string',
                'clear' => blank($data['monnify_contract_code'] ?? null),
            ],
            [
                'section' => 'payments.monnify',
                'key' => 'timeout',
                'value' => (int) ($data['monnify_timeout'] ?? 30),
                'type' => 'integer',
                'clear' => false,
            ],
            [
                'section' => 'payments.paystack',
                'key' => 'base_url',
                'value' => $data['paystack_base_url'] ?? null,
                'type' => 'string',
                'clear' => blank($data['paystack_base_url'] ?? null),
            ],
            [
                'section' => 'payments.paystack',
                'key' => 'timeout',
                'value' => (int) ($data['paystack_timeout'] ?? 30),
                'type' => 'integer',
                'clear' => false,
            ],
            [
                'section' => 'payments.paymentpoint',
                'key' => 'base_url',
                'value' => $data['paymentpoint_base_url'] ?? null,
                'type' => 'string',
                'clear' => blank($data['paymentpoint_base_url'] ?? null),
            ],
            [
                'section' => 'payments.paymentpoint',
                'key' => 'timeout',
                'value' => (int) ($data['paymentpoint_timeout'] ?? 30),
                'type' => 'integer',
                'clear' => false,
            ],
            [
                'section' => 'payments.paymentpoint',
                'key' => 'business_id',
                'value' => $data['paymentpoint_business_id'] ?? null,
                'type' => 'string',
                'clear' => blank($data['paymentpoint_business_id'] ?? null),
            ],
            [
                'section' => 'payments.paymentpoint',
                'key' => 'bank_codes',
                'value' => (function () use ($data) {
                    $codes = array_values(array_filter(array_map(
                        fn ($code) => trim((string) $code),
                        explode(',', (string) ($data['paymentpoint_bank_codes_csv'] ?? ''))
                    )));

                    return $codes !== [] ? $codes : ['20946', '20897'];
                })(),
                'type' => 'json',
                'clear' => false,
            ],
            [
                'section' => 'payments.methods',
                'key' => 'monnify_reserved_account_enabled',
                'value' => (bool) ($data['monnify_reserved_account_enabled'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'payments.methods',
                'key' => 'paymentpoint_reserved_account_enabled',
                'value' => (bool) ($data['paymentpoint_reserved_account_enabled'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'payments.methods',
                'key' => 'monnify_instant_checkout_enabled',
                'value' => (bool) ($data['monnify_instant_checkout_enabled'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'payments.methods',
                'key' => 'paystack_checkout_enabled',
                'value' => (bool) ($data['paystack_checkout_enabled'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'payments.methods',
                'key' => 'require_bvn_for_monnify',
                'value' => (bool) ($data['require_bvn_for_monnify'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'admin.reports',
                'key' => 'daily_enabled',
                'value' => (bool) ($data['admin_report_daily_enabled'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'admin.reports',
                'key' => 'weekly_enabled',
                'value' => (bool) ($data['admin_report_weekly_enabled'] ?? false),
                'type' => 'boolean',
                'clear' => false,
            ],
            [
                'section' => 'admin.reports',
                'key' => 'recipients_csv',
                'value' => trim((string) ($data['admin_report_recipients'] ?? '')),
                'type' => 'string',
                'clear' => blank($data['admin_report_recipients'] ?? null),
            ],
            [
                'section' => 'webhooks.monnify',
                'key' => 'signature_header',
                'value' => $data['monnify_signature_header'] ?? null,
                'type' => 'string',
                'clear' => blank($data['monnify_signature_header'] ?? null),
            ],
            [
                'section' => 'webhooks.paymentpoint',
                'key' => 'signature_header',
                'value' => $data['paymentpoint_signature_header'] ?? null,
                'type' => 'string',
                'clear' => blank($data['paymentpoint_signature_header'] ?? null),
            ],
        ];

        $this->appendSecretEntry($entries, 'payments.monnify', 'api_key', $data, 'monnify_api_key', 'clear_monnify_api_key');
        $this->appendSecretEntry($entries, 'payments.monnify', 'secret_key', $data, 'monnify_secret_key', 'clear_monnify_secret_key');
        $this->appendSecretEntry($entries, 'payments.paystack', 'public_key', $data, 'paystack_public_key', 'clear_paystack_public_key');
        $this->appendSecretEntry($entries, 'payments.paystack', 'secret_key', $data, 'paystack_secret_key', 'clear_paystack_secret_key');
        $this->appendSecretEntry($entries, 'payments.paymentpoint', 'api_key', $data, 'paymentpoint_api_key', 'clear_paymentpoint_api_key');
        $this->appendSecretEntry($entries, 'payments.paymentpoint', 'secret_key', $data, 'paymentpoint_secret_key', 'clear_paymentpoint_secret_key');
        $this->appendSecretEntry($entries, 'webhooks.monnify', 'secret', $data, 'monnify_webhook_secret', 'clear_monnify_webhook_secret');
        $this->appendSecretEntry($entries, 'webhooks.smeplug', 'secret', $data, 'smeplug_webhook_secret', 'clear_smeplug_webhook_secret');
        $this->appendSecretEntry($entries, 'webhooks.paystack', 'secret', $data, 'paystack_webhook_secret', 'clear_paystack_webhook_secret');
        $this->appendSecretEntry($entries, 'webhooks.paymentpoint', 'secret', $data, 'paymentpoint_webhook_secret', 'clear_paymentpoint_webhook_secret');

        $systemSettingService->putMany($entries);

        $new = $this->snapshotForAudit($systemSettingService);

        $auditLogService->log(
            admin: $request->user(),
            action: 'integration_settings.updated',
            entity: 'system_settings',
            entityId: 'integrations',
            oldValues: $old,
            newValues: $new,
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', 'Integration settings updated.');
    }

    protected function appendSecretEntry(
        array &$entries,
        string $section,
        string $settingKey,
        array $data,
        string $inputKey,
        string $clearKey
    ): void {
        $clear = (bool) ($data[$clearKey] ?? false);
        $value = $data[$inputKey] ?? null;

        if ($clear) {
            $entries[] = [
                'section' => $section,
                'key' => $settingKey,
                'clear' => true,
            ];

            return;
        }

        if (filled($value)) {
            $entries[] = [
                'section' => $section,
                'key' => $settingKey,
                'value' => (string) $value,
                'type' => 'string',
                'is_secret' => true,
            ];
        }
    }

    protected function snapshotForAudit(SystemSettingService $systemSettingService): array
    {
        return [
            'payments.monnify.base_url' => (string) $systemSettingService->get('payments.monnify', 'base_url', config('services.monnify.base_url')),
            'payments.monnify.contract_code' => (string) $systemSettingService->get('payments.monnify', 'contract_code', config('services.monnify.contract_code')),
            'payments.monnify.timeout' => (int) $systemSettingService->get('payments.monnify', 'timeout', config('services.monnify.timeout', 30)),
            'payments.monnify.api_key_set' => $systemSettingService->has('payments.monnify', 'api_key') || filled(config('services.monnify.api_key')),
            'payments.monnify.secret_key_set' => $systemSettingService->has('payments.monnify', 'secret_key') || filled(config('services.monnify.secret_key')),
            'payments.paystack.base_url' => (string) $systemSettingService->get('payments.paystack', 'base_url', config('services.paystack.base_url', 'https://api.paystack.co')),
            'payments.paystack.timeout' => (int) $systemSettingService->get('payments.paystack', 'timeout', config('services.paystack.timeout', 30)),
            'payments.paystack.public_key_set' => $systemSettingService->has('payments.paystack', 'public_key') || filled(config('services.paystack.public_key')),
            'payments.paystack.secret_key_set' => $systemSettingService->has('payments.paystack', 'secret_key') || filled(config('services.paystack.secret_key')),
            'payments.paymentpoint.base_url' => (string) $systemSettingService->get('payments.paymentpoint', 'base_url', config('services.paymentpoint.base_url', 'https://api.paymentpoint.co')),
            'payments.paymentpoint.timeout' => (int) $systemSettingService->get('payments.paymentpoint', 'timeout', config('services.paymentpoint.timeout', 30)),
            'payments.paymentpoint.business_id' => (string) $systemSettingService->get('payments.paymentpoint', 'business_id', config('services.paymentpoint.business_id')),
            'payments.paymentpoint.bank_codes' => array_values((array) $systemSettingService->get('payments.paymentpoint', 'bank_codes', config('services.paymentpoint.bank_codes', ['20946', '20897']))),
            'payments.paymentpoint.api_key_set' => $systemSettingService->has('payments.paymentpoint', 'api_key') || filled(config('services.paymentpoint.api_key')),
            'payments.paymentpoint.secret_key_set' => $systemSettingService->has('payments.paymentpoint', 'secret_key') || filled(config('services.paymentpoint.secret_key')),
            'payments.methods.monnify_reserved_account_enabled' => (bool) $systemSettingService->get('payments.methods', 'monnify_reserved_account_enabled', true),
            'payments.methods.paymentpoint_reserved_account_enabled' => (bool) $systemSettingService->get('payments.methods', 'paymentpoint_reserved_account_enabled', false),
            'payments.methods.monnify_instant_checkout_enabled' => (bool) $systemSettingService->get('payments.methods', 'monnify_instant_checkout_enabled', false),
            'payments.methods.paystack_checkout_enabled' => (bool) $systemSettingService->get('payments.methods', 'paystack_checkout_enabled', false),
            'payments.methods.require_bvn_for_monnify' => (bool) $systemSettingService->get('payments.methods', 'require_bvn_for_monnify', false),
            'admin.reports.daily_enabled' => (bool) $systemSettingService->get('admin.reports', 'daily_enabled', false),
            'admin.reports.weekly_enabled' => (bool) $systemSettingService->get('admin.reports', 'weekly_enabled', false),
            'admin.reports.recipients_set' => filled((string) $systemSettingService->get('admin.reports', 'recipients_csv', '')),
            'webhooks.monnify.signature_header' => (string) $systemSettingService->get('webhooks.monnify', 'signature_header', config('vtu.webhooks.monnify.signature_header', 'monnify-signature')),
            'webhooks.monnify.secret_set' => $systemSettingService->has('webhooks.monnify', 'secret') || filled(config('vtu.webhooks.monnify.secret')),
            'webhooks.paymentpoint.signature_header' => (string) $systemSettingService->get('webhooks.paymentpoint', 'signature_header', config('vtu.webhooks.paymentpoint.signature_header', 'Paymentpoint-Signature')),
            'webhooks.paymentpoint.secret_set' => $systemSettingService->has('webhooks.paymentpoint', 'secret') || filled(config('vtu.webhooks.paymentpoint.secret')),
            'webhooks.smeplug.secret_set' => $systemSettingService->has('webhooks.smeplug', 'secret') || filled(config('vtu.webhooks.smeplug.secret')),
            'webhooks.paystack.secret_set' => $systemSettingService->has('webhooks.paystack', 'secret') || filled(config('vtu.webhooks.paystack.secret')),
        ];
    }

    protected function validateHttpsUrl(string $value, string $attribute, callable $fail): void
    {
        $parts = parse_url($value);

        if (! is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
            $fail("The {$attribute} must be a valid URL.");

            return;
        }

        if (strtolower((string) $parts['scheme']) !== 'https') {
            $fail("The {$attribute} must use HTTPS.");
        }

        $host = strtolower((string) $parts['host']);
        if (in_array($host, ['localhost', '127.0.0.1', '::1'], true) || str_ends_with($host, '.local')) {
            $fail("The {$attribute} host is not allowed.");

            return;
        }

        if (filter_var($host, FILTER_VALIDATE_IP)) {
            if (! filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                $fail("The {$attribute} cannot target private or reserved IP ranges.");
            }

            return;
        }

        $resolved = gethostbynamel($host) ?: [];
        foreach ($resolved as $ip) {
            if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                $fail("The {$attribute} resolves to a private or reserved IP address.");

                return;
            }
        }
    }
}
