<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Http\Controllers\Concerns\AuthorizesProjectAccess;
use App\Http\Requests\Discussion\IndexDiscussionRequest;
use App\Http\Requests\Discussion\StoreDiscussionRequest;
use App\Http\Requests\Discussion\UpdateDiscussionRequest;
use App\Http\Resources\DiscussionResource;
use App\Models\Discussion;
use App\Models\DiscussionMessage;
use App\Models\DiscussionParticipant;
use App\Models\Project;
use App\Support\ApiResponse;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;

class DiscussionController extends Controller
{
    use AuthorizesProjectAccess;

    public function index(IndexDiscussionRequest $request)
    {
        Gate::authorize('viewAny', Discussion::class);

        $query = Discussion::query()
            ->with([
                'project:id,code,name',
                'createdBy',
                'latestMessage.user',
            ])
            ->withCount(['messages', 'participants'])
            ->latest();

        $this->restrictVisibility($query, $request->user());
        $this->applyFilters($query, $request);

        if (! $request->filled('status')) {
            $query->active();
        }

        $perPage = min($request->integer('per_page', 15), 50);

        return DiscussionResource::collection(
            $request->boolean('noPaginate') ? $query->get() : $query->paginate($perPage)
        );
    }

    public function history(IndexDiscussionRequest $request)
    {
        Gate::authorize('viewAny', Discussion::class);

        $query = Discussion::query()
            ->history()
            ->with([
                'project:id,code,name',
                'createdBy',
                'latestMessage.user',
            ])
            ->withCount(['messages', 'participants'])
            ->latest();

        $this->restrictVisibility($query, $request->user());
        $this->applyFilters($query, $request);

        $perPage = min($request->integer('per_page', 15), 50);

        return DiscussionResource::collection(
            $request->boolean('noPaginate') ? $query->get() : $query->paginate($perPage)
        );
    }

    public function store(StoreDiscussionRequest $request)
    {
        $project = Project::with('producer')->findOrFail($request->integer('project_id'));

        Gate::authorize('create', [Discussion::class, $project]);
        $this->authorizeProjectAccess($project);

        try {
            DB::beginTransaction();

            $discussion = Discussion::create([
                'project_id' => $project->id,
                'title' => $request->input('title'),
                'hashtag' => $request->input('hashtag'),
                'initial_message' => $request->input('initial_message'),
                'status' => Discussion::STATUS_ACTIVE,
                'created_by' => $request->user()->id,
            ]);

            DiscussionMessage::create([
                'discussion_id' => $discussion->id,
                'user_id' => $request->user()->id,
                'body' => $request->input('initial_message'),
                'hashtag' => $request->input('hashtag'),
            ]);

            $userIds = $request->input('participant_mode') === 'all_project'
                ? Discussion::projectUserIds($project)
                : array_map('intval', $request->input('user_ids', []));

            $userIds[] = $request->user()->id;
            $userIds = array_values(array_unique($userIds));

            foreach ($userIds as $userId) {
                DiscussionParticipant::create([
                    'discussion_id' => $discussion->id,
                    'user_id' => $userId,
                    'is_blocked' => false,
                ]);
            }

            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();

            return ApiResponse::response(message: $exception->getMessage(), status: 419);
        }

        $discussion->load([
            'project:id,code,name',
            'createdBy',
            'latestMessage.user',
            'messages.user',
            'participants.user',
        ])->loadCount(['messages', 'participants']);

        return ApiResponse::created(
            new DiscussionResource($discussion),
            'بحث موضوعی با موفقیت ایجاد شد.'
        );
    }

    public function show(Discussion $discussion)
    {
        Gate::authorize('view', $discussion);

        $discussion->load([
            'project:id,code,name',
            'createdBy',
            'latestMessage.user',
            'messages' => fn ($query) => $query->with('user')->oldest(),
            'participants.user',
        ])->loadCount(['messages', 'participants']);

        return new DiscussionResource($discussion);
    }

    public function update(UpdateDiscussionRequest $request, Discussion $discussion)
    {
        Gate::authorize('update', $discussion);

        $discussion->update($request->safe()->only(['title', 'hashtag']));

        $discussion->load([
            'project:id,code,name',
            'createdBy',
            'latestMessage.user',
        ])->loadCount(['messages', 'participants']);

        return ApiResponse::success(
            new DiscussionResource($discussion),
            'بحث موضوعی با موفقیت ویرایش شد.'
        );
    }

    public function end(Discussion $discussion)
    {
        Gate::authorize('end', $discussion);

        $discussion->update(['status' => Discussion::STATUS_ENDED]);

        $discussion->load([
            'project:id,code,name',
            'createdBy',
            'latestMessage.user',
        ])->loadCount(['messages', 'participants']);

        return ApiResponse::success(
            new DiscussionResource($discussion),
            'بحث موضوعی پایان یافت.'
        );
    }

    public function destroy(Discussion $discussion)
    {
        Gate::authorize('delete', $discussion);

        $discussion->update(['status' => Discussion::STATUS_DELETED]);

        return ApiResponse::success(
            new DiscussionResource($discussion->load([
                'project:id,code,name',
                'createdBy',
            ])),
            'بحث موضوعی حذف شد.'
        );
    }

    private function restrictVisibility(Builder $query, $user): void
    {
        if ($user->hasRole('admin')) {
            return;
        }

        $query->where(function (Builder $builder) use ($user): void {
            $builder->where('created_by', $user->id)
                ->orWhereHas('participants', fn (Builder $p) => $p->where('user_id', $user->id))
                ->orWhereHas('project', function (Builder $projectQuery) use ($user): void {
                    $projectQuery->where('user_id', $user->id)
                        ->orWhere('created_by', $user->id)
                        ->orWhereHas('producer', fn (Builder $producerQuery) => $producerQuery->where('user_id', $user->id));
                });
        });
    }

    private function applyFilters(Builder $query, IndexDiscussionRequest $request): void
    {
        if ($request->filled('project_id')) {
            $query->where('project_id', $request->integer('project_id'));
        }

        if ($request->filled('project')) {
            $term = $request->input('project');
            $query->whereHas('project', function (Builder $projectQuery) use ($term): void {
                $projectQuery->where('name', 'like', '%' . $term . '%')
                    ->orWhere('code', 'like', '%' . $term . '%');
            });
        }

        if ($request->filled('hashtag')) {
            $hashtag = ltrim((string) $request->input('hashtag'), '#');
            $query->where(function (Builder $builder) use ($hashtag): void {
                $builder->where('hashtag', 'like', '%' . $hashtag . '%')
                    ->orWhere('hashtag', 'like', '%#' . $hashtag . '%');
            });
        }

        $name = $request->input('name') ?? $request->input('search');
        if ($name) {
            $term = '%' . $name . '%';
            $query->where(function (Builder $builder) use ($term): void {
                $builder->where('title', 'like', $term)
                    ->orWhere('hashtag', 'like', $term)
                    ->orWhere('initial_message', 'like', $term)
                    ->orWhereHas('messages', function (Builder $messages) use ($term): void {
                        $messages->where('body', 'like', $term)
                            ->orWhere('hashtag', 'like', $term);
                    });
            });
        }

        if ($request->filled('status')) {
            $query->where('status', $request->input('status'));
        }

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

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