<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1;

use App\Enums\UserRole;
use App\Http\Controllers\Controller;
use App\Http\Requests\ListAuditLogsRequest;
use App\Http\Resources\AuditLogResource;
use App\Models\AuditLog;
use App\Models\Group;
use App\Models\Household;
use App\Models\Member;
use App\Models\User;
use App\Services\BranchAccessService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;

class AuditLogController extends Controller
{
    public function __construct(private readonly BranchAccessService $branchAccess) {}

    public function index(ListAuditLogsRequest $request): JsonResponse
    {
        $user = $request->user();
        $query = AuditLog::query()->with('user:id,name')->orderByDesc('created_at')->orderByDesc('id');

        if ($user->role !== UserRole::SuperAdmin) {
            $this->applyBranchScope($query, $user);
        }

        if ($request->filled('member_id')) {
            $query->where('auditable_type', Member::class)->where('auditable_id', $request->integer('member_id'));
        }

        if ($request->filled('user_id')) {
            $query->where('user_id', $request->integer('user_id'));
        }

        if ($request->filled('action')) {
            $query->where('action', $request->string('action'));
        }

        if ($request->filled('date_from')) {
            $query->where('created_at', '>=', $request->date('date_from'));
        }

        if ($request->filled('date_to')) {
            $query->where('created_at', '<=', $request->date('date_to')->endOfDay());
        }

        $perPage = (int) ($request->integer('per_page') ?: 25);
        $logs = $query->cursorPaginate($perPage)->withQueryString();

        $this->attachLabels($logs->items());

        return AuditLogResource::collection($logs)->response();
    }

    private function applyBranchScope(Builder $query, User $user): void
    {
        $branchIds = $this->branchAccess->accessibleBranchIds($user);

        if ($branchIds === null) {
            return;
        }

        $memberIds = Member::withTrashed()->whereIn('branch_id', $branchIds)->pluck('id');
        $householdIds = Household::withTrashed()->whereIn('branch_id', $branchIds)->pluck('id');
        $groupIds = Group::withTrashed()->where(
            fn (Builder $q) => $q->whereIn('branch_id', $branchIds)->orWhereNull('branch_id'),
        )->pluck('id');

        $query->where(function (Builder $q) use ($memberIds, $householdIds, $groupIds) {
            $q->where(fn (Builder $q2) => $q2->where('auditable_type', Member::class)->whereIn('auditable_id', $memberIds))
                ->orWhere(fn (Builder $q2) => $q2->where('auditable_type', Household::class)->whereIn('auditable_id', $householdIds))
                ->orWhere(fn (Builder $q2) => $q2->where('auditable_type', Group::class)->whereIn('auditable_id', $groupIds));
        });
    }

    /**
     * @param  array<int, AuditLog>  $logs
     */
    private function attachLabels(array $logs): void
    {
        $memberIds = [];

        foreach ($logs as $log) {
            if ($log->auditable_type === Member::class) {
                $memberIds[] = $log->auditable_id;
            }
        }

        if ($memberIds === []) {
            return;
        }

        $names = Member::withTrashed()->whereIn('id', $memberIds)->get(['id', 'first_name', 'last_name'])
            ->mapWithKeys(fn (Member $member) => [$member->id => trim("{$member->first_name} {$member->last_name}")]);

        foreach ($logs as $log) {
            if ($log->auditable_type === Member::class) {
                $log->auditable_label = $names[$log->auditable_id] ?? null;
            }
        }
    }
}
