<?php

namespace App\Services\Payments;

use App\Models\PaymentEvent;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class PaymentLogger
{
    protected array $context = [];

    public function __construct()
    {
        $this->context = [
            'request_id' => uniqid('pay_', true),
            'timestamp' => now()->toISOString(),
            'user_agent' => $this->getUserAgent(),
            'ip_address' => $this->getIpAddress(),
        ];
    }

    /**
     * Log a payment event
     */
    public function logEvent(string $eventType, array $data = [], ?string $level = 'info'): void
    {
        $eventData = array_merge($this->context, [
            'event_type' => $eventType,
            'user_id' => $this->getUserId(),
            'data' => $data,
            'level' => $level,
        ]);

        // Log to Laravel logs
        $this->logToFile("Payment event: {$eventType}", $eventData, $level);

        // Store in database for audit trail
        $this->storeEvent($eventType, $data, $level);
    }

    /**
     * Log an error event
     */
    public function logError(string $eventType, array $data = [], ?\Exception $exception = null): void
    {
        $errorData = $data;

        if ($exception) {
            $errorData['exception'] = [
                'message' => $exception->getMessage(),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'trace' => $exception->getTraceAsString(),
            ];
        }

        $this->logEvent($eventType, $errorData, 'error');
    }

    /**
     * Log a security event
     */
    public function logSecurityEvent(string $eventType, array $data = []): void
    {
        $securityData = array_merge($data, [
            'security_level' => 'high',
            'requires_review' => true,
        ]);

        $this->logEvent("security_{$eventType}", $securityData, 'warning');

        // Additional security logging
        $this->logToFile("Security payment event: {$eventType}", array_merge($this->context, $securityData), 'warning');
    }

    /**
     * Log webhook events
     */
    public function logWebhook(string $provider, string $eventType, array $payload, bool $success = true): void
    {
        $webhookData = [
            'provider' => $provider,
            'webhook_event_type' => $eventType,
            'payload_size' => strlen(json_encode($payload)),
            'payload_hash' => hash('sha256', json_encode($payload)),
            'success' => $success,
        ];

        // Don't store full payload in logs for security/size reasons
        $this->logEvent('webhook_received', $webhookData, $success ? 'info' : 'error');

        // Store full payload in database for debugging (with retention policy)
        $this->storeWebhookPayload($provider, $eventType, $payload, $success);
    }

    /**
     * Log subscription lifecycle events
     */
    public function logSubscriptionEvent(string $action, int $subscriptionId, array $data = []): void
    {
        $subscriptionData = array_merge($data, [
            'subscription_id' => $subscriptionId,
            'action' => $action,
        ]);

        $this->logEvent("subscription_{$action}", $subscriptionData);
    }

    /**
     * Log payment method events
     */
    public function logPaymentMethodEvent(string $action, array $data = []): void
    {
        $this->logEvent("payment_method_{$action}", $data);
    }

    /**
     * Log provider events
     */
    public function logProviderEvent(string $provider, string $action, array $data = []): void
    {
        $providerData = array_merge($data, [
            'provider' => $provider,
            'provider_action' => $action,
        ]);

        $this->logEvent("provider_{$action}", $providerData);
    }

    /**
     * Log admin actions
     */
    public function logAdminAction(string $action, array $data = []): void
    {
        $adminData = array_merge($data, [
            'admin_user_id' => Auth::id(),
            'admin_action' => $action,
            'requires_review' => in_array($action, ['refund', 'subscription_override', 'provider_config_change']),
        ]);

        $this->logEvent("admin_{$action}", $adminData);
    }

    /**
     * Log migration events
     */
    public function logMigrationEvent(string $action, array $data = []): void
    {
        $migrationData = array_merge($data, [
            'migration_action' => $action,
            'batch_id' => $data['batch_id'] ?? null,
        ]);

        $this->logEvent("migration_{$action}", $migrationData);
    }

    /**
     * Log compliance events
     */
    public function logComplianceEvent(string $type, array $data = []): void
    {
        $complianceData = array_merge($data, [
            'compliance_type' => $type,
            'retention_required' => true,
            'gdpr_relevant' => in_array($type, ['data_access', 'data_deletion', 'consent_withdrawn']),
        ]);

        $this->logEvent("compliance_{$type}", $complianceData);
    }

    /**
     * Get audit trail for a specific user
     */
    public function getUserAuditTrail(int $userId, array $filters = []): array
    {
        $query = PaymentEvent::where('user_id', $userId);

        if (! empty($filters['event_type'])) {
            $query->where('event_type', $filters['event_type']);
        }

        if (! empty($filters['date_from'])) {
            $query->where('created_at', '>=', $filters['date_from']);
        }

        if (! empty($filters['date_to'])) {
            $query->where('created_at', '<=', $filters['date_to']);
        }

        if (! empty($filters['level'])) {
            $query->where('level', $filters['level']);
        }

        return $query->orderBy('created_at', 'desc')
            ->limit($filters['limit'] ?? 1000)
            ->get()
            ->toArray();
    }

    /**
     * Get audit trail for a subscription
     */
    public function getSubscriptionAuditTrail(int $subscriptionId): array
    {
        return PaymentEvent::whereJsonContains('data->subscription_id', $subscriptionId)
            ->orderBy('created_at', 'desc')
            ->get()
            ->toArray();
    }

    /**
     * Get provider audit trail
     */
    public function getProviderAuditTrail(string $provider, array $filters = []): array
    {
        $query = PaymentEvent::whereJsonContains('data->provider', $provider);

        if (! empty($filters['date_from'])) {
            $query->where('created_at', '>=', $filters['date_from']);
        }

        if (! empty($filters['date_to'])) {
            $query->where('created_at', '<=', $filters['date_to']);
        }

        return $query->orderBy('created_at', 'desc')
            ->limit($filters['limit'] ?? 1000)
            ->get()
            ->toArray();
    }

    /**
     * Generate compliance report
     */
    public function generateComplianceReport(array $criteria = []): array
    {
        $query = PaymentEvent::query();

        if (! empty($criteria['date_from'])) {
            $query->where('created_at', '>=', $criteria['date_from']);
        }

        if (! empty($criteria['date_to'])) {
            $query->where('created_at', '<=', $criteria['date_to']);
        }

        if (! empty($criteria['event_types'])) {
            $query->whereIn('event_type', $criteria['event_types']);
        }

        $events = $query->get();

        return [
            'report_generated_at' => now()->toISOString(),
            'criteria' => $criteria,
            'total_events' => $events->count(),
            'events_by_type' => $events->groupBy('event_type')->map->count(),
            'events_by_level' => $events->groupBy('level')->map->count(),
            'security_events' => $events->filter(fn ($e) => str_contains($e->event_type, 'security'))->count(),
            'compliance_events' => $events->filter(fn ($e) => str_contains($e->event_type, 'compliance'))->count(),
            'retention_summary' => $this->getRetentionSummary($events),
        ];
    }

    /**
     * Store event in database
     */
    protected function storeEvent(string $eventType, array $data, string $level): void
    {
        try {
            PaymentEvent::create([
                'event_type' => $eventType,
                'user_id' => Auth::id(),
                'level' => $level,
                'data' => array_merge($this->context, $data),
                'ip_address' => $this->context['ip_address'],
                'user_agent' => $this->context['user_agent'],
                'request_id' => $this->context['request_id'],
            ]);
        } catch (\Exception $e) {
            // Fallback to file logging if database fails
            $this->logToFile('Failed to store payment event in database', [
                'event_type' => $eventType,
                'error' => $e->getMessage(),
                'data' => $data,
            ], 'error');
        }
    }

    /**
     * Store webhook payload
     */
    protected function storeWebhookPayload(string $provider, string $eventType, array $payload, bool $success): void
    {
        try {
            PaymentEvent::create([
                'event_type' => 'webhook_payload',
                'level' => $success ? 'info' : 'error',
                'data' => [
                    'provider' => $provider,
                    'webhook_event_type' => $eventType,
                    'payload' => $payload,
                    'success' => $success,
                    'stored_at' => now()->toISOString(),
                ] + $this->context,
                'ip_address' => $this->context['ip_address'],
                'user_agent' => $this->context['user_agent'],
                'request_id' => $this->context['request_id'],
                'expires_at' => now()->addDays(30), // Webhook payloads expire after 30 days
            ]);
        } catch (\Exception $e) {
            $this->logToFile('Failed to store webhook payload', [
                'provider' => $provider,
                'event_type' => $eventType,
                'error' => $e->getMessage(),
            ], 'error');
        }
    }

    /**
     * Get retention summary for compliance
     */
    protected function getRetentionSummary($events): array
    {
        $now = now();
        $retentionPeriods = [
            '30_days' => $now->copy()->subDays(30),
            '90_days' => $now->copy()->subDays(90),
            '1_year' => $now->copy()->subYear(),
            '7_years' => $now->copy()->subYears(7),
        ];

        return array_map(static function ($date) use ($events) {
            return $events->where('created_at', '>=', $date)->count();
        }, $retentionPeriods);
    }

    /**
     * Clean up old events based on retention policy
     */
    public function cleanupOldEvents(): array
    {
        $cleanupResults = [];

        // Clean up webhook payloads after 30 days
        $webhookCleanup = PaymentEvent::where('event_type', 'webhook_payload')
            ->where('expires_at', '<', now())
            ->delete();

        $cleanupResults['webhook_payloads'] = $webhookCleanup;

        // Clean up debug events after 90 days
        $debugCleanup = PaymentEvent::where('level', 'debug')
            ->where('created_at', '<', now()->subDays(90))
            ->delete();

        $cleanupResults['debug_events'] = $debugCleanup;

        // Keep compliance and security events for 7 years
        // This is handled by database retention policies

        $this->logToFile('Payment event cleanup completed', $cleanupResults, 'info');

        return $cleanupResults;
    }

    /**
     * Set additional context for logging
     */
    public function setContext(array $context): void
    {
        $this->context = array_merge($this->context, $context);
    }

    /**
     * Get current context
     */
    public function getContext(): array
    {
        return $this->context;
    }

    /**
     * Get user agent safely
     */
    protected function getUserAgent(): ?string
    {
        try {
            return request()->userAgent();
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * Get IP address safely
     */
    protected function getIpAddress(): ?string
    {
        try {
            return request()->ip();
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * Get user ID safely
     */
    protected function getUserId(): ?int
    {
        try {
            return Auth::id();
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * Log to file safely
     */
    protected function logToFile(string $message, array $context, string $level): void
    {
        try {
            Log::{$level}($message, $context);
        } catch (\Exception $e) {
            // Silently fail if logging isn't available
        }
    }
}
