<?php

namespace Modules\SystemCore\App\Services;

use App\Services\Settings\ModuleSettingsService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Modules\SystemCore\App\Models\PostingLog;

class PostingLogService
{
    protected ModuleSettingsService $settingsService;

    public function __construct(ModuleSettingsService $settingsService)
    {
        $this->settingsService = $settingsService;
    }

    /**
     * Check if posting log is enabled
     */
    public function isEnabled(): bool
    {
        return (bool) $this->settingsService->get('SystemCore', 'enable_posting_log', true);
    }

    /**
     * Get max retry attempts from settings
     */
    public function getMaxRetryAttempts(): int
    {
        return (int) $this->settingsService->get('SystemCore', 'max_retry_attempts', 3);
    }

    /**
     * Get log retention days from settings
     */
    public function getRetentionDays(): int
    {
        return (int) $this->settingsService->get('SystemCore', 'posting_log_retention_days', 90);
    }

    /**
     * Log a successful posting operation
     */
    public function logSuccess(
        string $sourceModule,
        string $sourceType,
        int $sourceId,
        string $targetModule,
        string $targetType,
        int $targetId,
        string $action,
        ?string $description = null,
        ?float $amount = null,
        ?array $oldValues = null,
        ?array $newValues = null
    ): ?PostingLog {
        if (! $this->isEnabled()) {
            return null;
        }

        return PostingLog::query()->create([
            'source_module' => $sourceModule,
            'source_entity_type' => $sourceType,
            'source_entity_id' => $sourceId,
            'target_module' => $targetModule,
            'target_entity_type' => $targetType,
            'target_entity_id' => $targetId,
            'action' => $action,
            'status' => 'success',
            'description' => $description,
            'amount' => $amount,
            'old_values' => $oldValues,
            'new_values' => $newValues,
            'posted_by' => Auth::id(),
            'posted_at' => now(),
        ]);
    }

    /**
     * Log a failed posting operation
     */
    public function logFailure(
        string $sourceModule,
        string $sourceType,
        int $sourceId,
        string $targetModule,
        string $targetType,
        string $action,
        string $errorMessage,
        ?string $description = null,
        ?float $amount = null
    ): ?PostingLog {
        if (! $this->isEnabled()) {
            return null;
        }

        return PostingLog::query()->create([
            'source_module' => $sourceModule,
            'source_entity_type' => $sourceType,
            'source_entity_id' => $sourceId,
            'target_module' => $targetModule,
            'target_entity_type' => $targetType,
            'target_entity_id' => null,
            'action' => $action,
            'status' => 'failed',
            'description' => $description,
            'amount' => $amount,
            'error_message' => $errorMessage,
            'posted_by' => Auth::id(),
            'posted_at' => now(),
        ]);
    }

    /**
     * Log a pending posting operation
     */
    public function logPending(
        string $sourceModule,
        string $sourceType,
        int $sourceId,
        string $targetModule,
        string $targetType,
        string $action,
        ?string $description = null,
        ?float $amount = null,
        ?array $newValues = null
    ): ?PostingLog {
        if (! $this->isEnabled()) {
            return null;
        }

        return PostingLog::query()->create([
            'source_module' => $sourceModule,
            'source_entity_type' => $sourceType,
            'source_entity_id' => $sourceId,
            'target_module' => $targetModule,
            'target_entity_type' => $targetType,
            'target_entity_id' => null,
            'action' => $action,
            'status' => 'pending',
            'description' => $description,
            'amount' => $amount,
            'new_values' => $newValues,
            'posted_by' => Auth::id(),
            'posted_at' => now(),
        ]);
    }

    /**
     * Mark a log as retried and increment retry count
     */
    public function markRetried(PostingLog $log): PostingLog
    {
        $log->update([
            'retry_count' => $log->retry_count + 1,
        ]);

        return $log->fresh();
    }

    /**
     * Mark a pending/failed log as successful
     */
    public function markSuccessful(PostingLog $log, int $targetId, ?array $newValues = null): PostingLog
    {
        $updateData = [
            'status' => 'success',
            'target_entity_id' => $targetId,
            'error_message' => null,
        ];

        if ($newValues !== null) {
            $updateData['new_values'] = $newValues;
        }

        $log->update($updateData);

        return $log->fresh();
    }

    /**
     * Mark a pending log as failed
     */
    public function markFailed(PostingLog $log, string $errorMessage): PostingLog
    {
        $log->update([
            'status' => 'failed',
            'error_message' => $errorMessage,
        ]);

        return $log->fresh();
    }

    /**
     * Get statistics for posting logs
     *
     * @return array{
     *     total: int,
     *     by_status: array<string, int>,
     *     by_source_module: array<string, int>,
     *     by_target_module: array<string, int>,
     *     by_action: array<string, int>,
     *     recent_failures: int,
     *     success_rate: float
     * }
     */
    public function getStatistics(): array
    {
        $total = PostingLog::query()->count();
        $successCount = PostingLog::query()->successful()->count();

        return [
            'total' => $total,
            'by_status' => PostingLog::query()
                ->select('status', DB::raw('count(*) as count'))
                ->groupBy('status')
                ->pluck('count', 'status')
                ->toArray(),
            'by_source_module' => PostingLog::query()
                ->select('source_module', DB::raw('count(*) as count'))
                ->groupBy('source_module')
                ->pluck('count', 'source_module')
                ->toArray(),
            'by_target_module' => PostingLog::query()
                ->select('target_module', DB::raw('count(*) as count'))
                ->groupBy('target_module')
                ->pluck('count', 'target_module')
                ->toArray(),
            'by_action' => PostingLog::query()
                ->select('action', DB::raw('count(*) as count'))
                ->groupBy('action')
                ->pluck('count', 'action')
                ->toArray(),
            'recent_failures' => PostingLog::query()
                ->failed()
                ->where('posted_at', '>=', now()->subDays(7))
                ->count(),
            'success_rate' => $total > 0 ? round(($successCount / $total) * 100, 2) : 0.0,
        ];
    }

    /**
     * Get logs for a specific source entity
     */
    public function getLogsForSource(string $module, string $entityType, int $entityId): \Illuminate\Database\Eloquent\Collection
    {
        return PostingLog::query()
            ->where('source_module', $module)
            ->where('source_entity_type', $entityType)
            ->where('source_entity_id', $entityId)
            ->orderBy('posted_at', 'desc')
            ->get();
    }

    /**
     * Get logs for a specific target entity
     */
    public function getLogsForTarget(string $module, string $entityType, int $entityId): \Illuminate\Database\Eloquent\Collection
    {
        return PostingLog::query()
            ->where('target_module', $module)
            ->where('target_entity_type', $entityType)
            ->where('target_entity_id', $entityId)
            ->orderBy('posted_at', 'desc')
            ->get();
    }

    /**
     * Get pending logs that can be retried
     */
    public function getPendingForRetry(?int $maxRetries = null): \Illuminate\Database\Eloquent\Collection
    {
        $maxRetries = $maxRetries ?? $this->getMaxRetryAttempts();

        return PostingLog::query()
            ->where('status', 'pending')
            ->where('retry_count', '<', $maxRetries)
            ->orderBy('posted_at', 'asc')
            ->get();
    }

    /**
     * Get failed logs that can be retried
     */
    public function getFailedForRetry(?int $maxRetries = null): \Illuminate\Database\Eloquent\Collection
    {
        $maxRetries = $maxRetries ?? $this->getMaxRetryAttempts();

        return PostingLog::query()
            ->where('status', 'failed')
            ->where('retry_count', '<', $maxRetries)
            ->orderBy('posted_at', 'asc')
            ->get();
    }

    /**
     * Purge old posting logs based on retention settings
     *
     * @return int Number of deleted records (0 if retention is set to keep forever)
     */
    public function purgeOldLogs(?int $retentionDays = null): int
    {
        $retentionDays = $retentionDays ?? $this->getRetentionDays();

        // 0 means keep forever - no purging
        if ($retentionDays === 0) {
            return 0;
        }

        $cutoffDate = now()->subDays($retentionDays);

        return PostingLog::query()
            ->where('posted_at', '<', $cutoffDate)
            ->delete();
    }
}
