<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

class AuditLogController extends Controller
{
    public function index(Request $request): Response
    {
        $query = $this->applyFilters($request);

        $auditLogs = $query->paginate(25)->withQueryString();

        // Get distinct actions for the filter dropdown
        $actions = AuditLog::query()
            ->distinct()
            ->orderBy('action')
            ->pluck('action');

        return Inertia::render('Admin/AuditLogs/Index', [
            'auditLogs' => $auditLogs,
            'actions' => $actions,
            'filters' => [
                'search' => $request->input('search', ''),
                'action' => $request->input('action', ''),
                'date_from' => $request->input('date_from', ''),
                'date_to' => $request->input('date_to', ''),
            ],
        ]);
    }

    public function export(Request $request): StreamedResponse
    {
        $request->validate([
            'format' => ['required', 'in:csv,json'],
            'search' => ['nullable', 'string', 'max:255'],
            'action' => ['nullable', 'string', 'max:255'],
            'date_from' => ['nullable', 'date'],
            'date_to' => ['nullable', 'date'],
        ]);

        $format = $request->input('format', 'csv');
        $query = $this->applyFilters($request);

        if ($format === 'json') {
            return $this->exportJson($query);
        }

        return $this->exportCsv($query);
    }

    /**
     * @param  Builder<AuditLog>  $query
     */
    private function exportCsv(Builder $query): StreamedResponse
    {
        $filename = 'audit-logs-'.now()->format('Y-m-d-His').'.csv';

        return response()->streamDownload(function () use ($query): void {
            $handle = fopen('php://output', 'w');

            if ($handle === false) {
                return;
            }

            fputcsv($handle, [
                'ID',
                'Date',
                'User',
                'User Email',
                'Action',
                'Resource Type',
                'Resource ID',
                'IP Address',
                'User Agent',
                'Changes Summary',
            ]);

            $query->chunk(500, function ($logs) use ($handle): void {
                foreach ($logs as $log) {
                    fputcsv($handle, [
                        $log->id,
                        $log->created_at->format('Y-m-d H:i:s'),
                        $log->user?->name ?? 'System',
                        $log->user?->email ?? '-',
                        $log->action,
                        $log->resource_type ?? '-',
                        $log->resource_id ?? '-',
                        $log->ip_address ?? '-',
                        $log->user_agent ?? '-',
                        $this->summarizeChanges($log->changes),
                    ]);
                }
            });

            fclose($handle);
        }, $filename, [
            'Content-Type' => 'text/csv',
        ]);
    }

    /**
     * @param  Builder<AuditLog>  $query
     */
    private function exportJson(Builder $query): StreamedResponse
    {
        $filename = 'audit-logs-'.now()->format('Y-m-d-His').'.json';

        return response()->streamDownload(function () use ($query): void {
            echo '[';

            $first = true;

            $query->chunk(500, function ($logs) use (&$first): void {
                foreach ($logs as $log) {
                    if (! $first) {
                        echo ',';
                    }

                    echo json_encode([
                        'id' => $log->id,
                        'date' => $log->created_at->format('Y-m-d H:i:s'),
                        'user' => $log->user ? [
                            'id' => $log->user->id,
                            'name' => $log->user->name,
                            'email' => $log->user->email,
                        ] : null,
                        'action' => $log->action,
                        'resource_type' => $log->resource_type,
                        'resource_id' => $log->resource_id,
                        'ip_address' => $log->ip_address,
                        'user_agent' => $log->user_agent,
                        'changes' => $log->changes,
                        'created_at' => $log->created_at->toIso8601String(),
                    ], JSON_PRETTY_PRINT);

                    $first = false;
                }
            });

            echo ']';
        }, $filename, [
            'Content-Type' => 'application/json',
        ]);
    }

    /**
     * @param  array<string, mixed>|null  $changes
     */
    private function summarizeChanges(?array $changes): string
    {
        if (! $changes || count($changes) === 0) {
            return '-';
        }

        // Check for per-field old/new format: {"plan": {"old": "Basic", "new": "Pro"}, ...}
        $hasPerFieldOldNew = false;

        foreach ($changes as $value) {
            if (is_array($value) && (array_key_exists('old', $value) || array_key_exists('new', $value))) {
                $hasPerFieldOldNew = true;

                break;
            }
        }

        if ($hasPerFieldOldNew) {
            $changedFields = [];

            foreach ($changes as $field => $value) {
                if (is_array($value) && array_key_exists('old', $value) && array_key_exists('new', $value)) {
                    if ($value['old'] !== $value['new']) {
                        $changedFields[] = $field;
                    }
                }
            }

            return $changedFields ? 'Changed: '.implode(', ', $changedFields) : 'No changes';
        }

        // Top-level old/new structure: {"old": {...}, "new": {...}}
        if (isset($changes['old']) || isset($changes['new'])) {
            $fields = [];

            if (isset($changes['new']) && is_array($changes['new'])) {
                $fields = array_keys($changes['new']);
            } elseif (isset($changes['old']) && is_array($changes['old'])) {
                $fields = array_keys($changes['old']);
            }

            return 'Changed: '.implode(', ', $fields);
        }

        // Top-level before/after structure
        if (isset($changes['before']) || isset($changes['after'])) {
            $fields = [];

            if (isset($changes['after']) && is_array($changes['after'])) {
                $fields = array_keys($changes['after']);
            } elseif (isset($changes['before']) && is_array($changes['before'])) {
                $fields = array_keys($changes['before']);
            }

            return 'Changed: '.implode(', ', $fields);
        }

        // Flat key-value pairs
        return 'Fields: '.implode(', ', array_keys($changes));
    }

    /**
     * @return Builder<AuditLog>
     */
    private function applyFilters(Request $request): Builder
    {
        $query = AuditLog::query()
            ->with('user:id,name,email')
            ->latest();

        // Search by user name/email, action, or resource type
        if ($search = $request->input('search')) {
            $query->where(function ($q) use ($search): void {
                $q->where('action', 'like', "%{$search}%")
                    ->orWhere('resource_type', 'like', "%{$search}%")
                    ->orWhere('ip_address', 'like', "%{$search}%")
                    ->orWhereHas('user', function ($userQuery) use ($search): void {
                        $userQuery->where('name', 'like', "%{$search}%")
                            ->orWhere('email', 'like', "%{$search}%");
                    });
            });
        }

        // Filter by action
        if ($action = $request->input('action')) {
            $query->where('action', $action);
        }

        // Filter by date range
        if ($dateFrom = $request->input('date_from')) {
            $query->whereDate('created_at', '>=', $dateFrom);
        }

        if ($dateTo = $request->input('date_to')) {
            $query->whereDate('created_at', '<=', $dateTo);
        }

        return $query;
    }
}
