<?php

declare(strict_types=1);

namespace ImovelZapAi\Visits\Presentation\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
use ImovelZapAi\Tenancy\Support\TenantContext;
use ImovelZapAi\Visits\Application\Actions\CancelVisitAction;
use ImovelZapAi\Visits\Application\Actions\CheckInVisitAction;
use ImovelZapAi\Visits\Application\Actions\CreateVisitRequestAction;
use ImovelZapAi\Visits\Application\Actions\RecordVisitOutcomeAction;
use ImovelZapAi\Visits\Application\Actions\RescheduleVisitAction;
use ImovelZapAi\Visits\Application\Actions\ScheduleVisitAction;
use ImovelZapAi\Visits\Application\Queries\GetBrokerAgendaQuery;
use ImovelZapAi\Visits\Application\Queries\GetVisitDashboardQuery;
use ImovelZapAi\Visits\Application\Queries\ListVisitsQuery;
use ImovelZapAi\Visits\Domain\Enums\VisitOutcome;
use ImovelZapAi\Visits\Domain\Exceptions\VisitException;
use ImovelZapAi\Visits\Infrastructure\Persistence\Eloquent\VisitModel;
use ImovelZapAi\Visits\Presentation\Http\Requests\StoreVisitRequest;
use ImovelZapAi\Visits\Presentation\Http\Requests\UpdateVisitScheduleRequest;

final class VisitWebController extends Controller
{
    public function index(Request $request, ListVisitsQuery $listQuery, GetVisitDashboardQuery $dashboardQuery): View
    {
        $tenantId = (string) TenantContext::id();

        return view('visits.index', [
            'dashboard' => $dashboardQuery->execute($tenantId),
            'visits' => $listQuery->execute($tenantId, [
                'status' => $request->query('status'),
                'per_page' => 20,
            ]),
        ]);
    }

    public function agenda(Request $request, GetBrokerAgendaQuery $query): View
    {
        $brokerId = (int) ($request->query('responsible_user_id') ?? $request->user()->id);

        return view('visits.agenda', [
            'visits' => $query->execute((string) TenantContext::id(), $brokerId),
            'brokerId' => $brokerId,
        ]);
    }

    public function create(Request $request): View
    {
        return view('visits.create', [
            'contactId' => $request->query('contact_id'),
            'propertyId' => $request->query('property_id'),
            'conversationId' => $request->query('conversation_id'),
            'propertyInterestId' => $request->query('property_interest_id'),
        ]);
    }

    public function store(StoreVisitRequest $request, CreateVisitRequestAction $action): RedirectResponse
    {
        $visit = $action->execute((string) TenantContext::id(), $request->user(), $request->validated());

        return redirect()
            ->route('visits.show', $visit)
            ->with('status', 'Solicitação de visita criada.');
    }

    public function show(string $visit): View
    {
        $model = VisitModel::query()
            ->where('tenant_id', TenantContext::id())
            ->whereKey($visit)
            ->with(['contact', 'property', 'responsible', 'reminders', 'logs', 'participants'])
            ->firstOrFail();

        return view('visits.show', [
            'visit' => $model,
            'outcomes' => VisitOutcome::cases(),
        ]);
    }

    public function schedule(string $visit, UpdateVisitScheduleRequest $request, ScheduleVisitAction $action): RedirectResponse
    {
        $model = $this->find($visit);

        try {
            $action->execute(
                $model,
                $request->user(),
                Carbon::parse($request->validated('scheduled_start')),
                $request->validated('scheduled_end') ? Carbon::parse($request->validated('scheduled_end')) : null,
                $request->validated('responsible_user_id'),
            );
        } catch (VisitException $e) {
            return back()->withErrors(['schedule' => $e->getMessage()]);
        }

        return back()->with('status', 'Visita agendada e confirmada.');
    }

    public function reschedule(string $visit, UpdateVisitScheduleRequest $request, RescheduleVisitAction $action): RedirectResponse
    {
        try {
            $action->execute(
                $this->find($visit),
                $request->user(),
                Carbon::parse($request->validated('scheduled_start')),
                $request->validated('scheduled_end') ? Carbon::parse($request->validated('scheduled_end')) : null,
            );
        } catch (VisitException $e) {
            return back()->withErrors(['reschedule' => $e->getMessage()]);
        }

        return back()->with('status', 'Visita remarcada.');
    }

    public function cancel(string $visit, Request $request, CancelVisitAction $action): RedirectResponse
    {
        try {
            $action->execute($this->find($visit), $request->user(), $request->input('reason'));
        } catch (VisitException $e) {
            return back()->withErrors(['cancel' => $e->getMessage()]);
        }

        return back()->with('status', 'Visita cancelada.');
    }

    public function checkIn(string $visit, Request $request, CheckInVisitAction $action): RedirectResponse
    {
        try {
            $action->execute($this->find($visit), $request->user(), notes: $request->input('notes'));
        } catch (VisitException $e) {
            return back()->withErrors(['check_in' => $e->getMessage()]);
        }

        return back()->with('status', 'Check-in registrado.');
    }

    public function outcome(string $visit, Request $request, RecordVisitOutcomeAction $action): RedirectResponse
    {
        $request->validate([
            'outcome' => ['required', 'in:'.implode(',', VisitOutcome::values())],
            'notes' => ['nullable', 'string'],
        ]);

        try {
            $action->execute(
                $this->find($visit),
                $request->user(),
                VisitOutcome::from((string) $request->input('outcome')),
                $request->input('notes'),
            );
        } catch (VisitException $e) {
            return back()->withErrors(['outcome' => $e->getMessage()]);
        }

        return back()->with('status', 'Resultado registrado.');
    }

    private function find(string $id): VisitModel
    {
        return VisitModel::query()
            ->where('tenant_id', TenantContext::id())
            ->whereKey($id)
            ->firstOrFail();
    }
}
