<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Enums\ConversationCommissionStatus;
use App\Enums\ConversationOfferStatus;
use App\Enums\ConversationStatus;
use App\Enums\ConversationVisitStatus;
use App\Enums\PropertyRequestStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\ConversationAction\CloseDealRequest;
use App\Http\Requests\ConversationAction\UpdateCommissionStatusRequest;
use App\Http\Requests\ConversationAction\UpdateOfferStatusRequest;
use App\Http\Requests\ConversationAction\UpdateVisitStatusRequest;
use App\Http\Resources\ConversationResource;
use App\Http\Resources\MessageResource;
use App\Models\Conversation;
use App\Models\PropertyRequest;
use App\Services\ConversationSystemMessageService;
use App\Services\ProfessionalProfileCounter;
use App\Services\ConversationWorkflowSynchronizer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;

class ConversationActionController extends Controller
{
    public function __construct(
        private readonly ConversationSystemMessageService $systemMessages,
    ) {}

    public function updateVisitStatus(UpdateVisitStatusRequest $request, Conversation $conversation): JsonResponse
    {
        $this->authorize('view', $conversation);
        $this->ensureConversationActionable($conversation);

        $user = $request->user();
        $visitStatus = ConversationVisitStatus::from($request->validated('visit_status'));
        $visitDate = $request->validated('visit_date');
        $visitTime = $request->validated('visit_time');
        $locationNote = $request->validated('location_note');

        DB::transaction(function () use ($conversation, $user, $visitStatus, $visitDate, $visitTime, $locationNote): void {
            $c = Conversation::query()->lockForUpdate()->findOrFail($conversation->id);
            $this->ensureConversationActionable($c);

            ConversationWorkflowSynchronizer::applyVisit($c, $visitStatus);

            if ($visitStatus === ConversationVisitStatus::Scheduled) {
                $dateStr = $visitDate !== null ? (string) $visitDate : '';
                $timeStr = $visitTime !== null ? (string) $visitTime : '';
                $timePart = $timeStr !== '' ? " à {$timeStr}" : '';
                $body = "L'agent « {$user->name} » a programmé une visite pour le {$dateStr}{$timePart}.";
                if ($locationNote !== null && $locationNote !== '') {
                    $body .= " Lieu / remarque : {$locationNote}.";
                }
                $this->systemMessages->record($c, $user, $body);
            } else {
                $label = $this->visitStatusFrenchLabel($visitStatus);
                $body = $this->systemMessages->visitMessage(
                    $user,
                    $label,
                    $visitDate !== null ? (string) $visitDate : null,
                    $visitTime !== null ? (string) $visitTime : null,
                    $locationNote,
                );
                $this->systemMessages->record($c, $user, $body);
            }

            $c->save();
        });

        return $this->conversationPayloadResponse($conversation);
    }

    public function updateCommissionStatus(UpdateCommissionStatusRequest $request, Conversation $conversation): JsonResponse
    {
        $this->authorize('view', $conversation);
        $this->ensureConversationActionable($conversation);

        $user = $request->user();
        $status = ConversationCommissionStatus::from($request->validated('commission_status'));
        $note = $request->validated('note');
        $total = $request->validated('commission_total');

        DB::transaction(function () use ($conversation, $user, $status, $note, $total): void {
            $c = Conversation::query()->lockForUpdate()->findOrFail($conversation->id);
            $this->ensureConversationActionable($c);

            ConversationWorkflowSynchronizer::applyCommission($c, $status);

            $label = $this->commissionStatusFrenchLabel($status);
            $totalFormatted = $total !== null ? number_format((float) $total, 0, ',', ' ') : null;
            $body = $this->systemMessages->commissionMessage($user, $label, $note, $totalFormatted);
            $this->systemMessages->record($c, $user, $body);

            $c->save();
        });

        return $this->conversationPayloadResponse($conversation);
    }

    public function updateOfferStatus(UpdateOfferStatusRequest $request, Conversation $conversation): JsonResponse
    {
        $this->authorize('view', $conversation);
        $this->ensureConversationActionable($conversation);

        $user = $request->user();
        $status = ConversationOfferStatus::from($request->validated('offer_status'));
        $note = $request->validated('note');
        $amount = $request->validated('offer_amount');
        $offerDate = $request->validated('offer_date');

        DB::transaction(function () use ($conversation, $user, $status, $note, $amount, $offerDate): void {
            $c = Conversation::query()->lockForUpdate()->findOrFail($conversation->id);
            $this->ensureConversationActionable($c);

            ConversationWorkflowSynchronizer::applyOffer($c, $status);

            $label = $this->offerStatusFrenchLabel($status);
            $amountFormatted = $amount !== null ? number_format((float) $amount, 0, ',', ' ') : null;
            $offerDateStr = $offerDate !== null ? (string) $offerDate : null;
            $body = $this->systemMessages->offerMessage($user, $label, $amountFormatted, $offerDateStr, $note);
            $this->systemMessages->record($c, $user, $body);

            $c->save();
        });

        return $this->conversationPayloadResponse($conversation);
    }

    public function closeDeal(CloseDealRequest $request, Conversation $conversation): JsonResponse
    {
        $this->authorize('view', $conversation);
        $this->ensureConversationActionable($conversation);

        $user = $request->user();
        $outcome = $request->validated('outcome');
        $reason = $request->validated('reason');

        DB::transaction(function () use ($conversation, $user, $outcome, $reason): void {
            $locked = Conversation::query()->lockForUpdate()->findOrFail($conversation->id);
            $this->ensureConversationActionable($locked);

            if ($outcome === 'deal_closed') {
                $locked->status = ConversationStatus::DealClosed;
                $label = 'affaire conclue';
            } else {
                $locked->status = ConversationStatus::DealCancelled;
                $label = 'affaire annulée';
            }

            $body = $this->systemMessages->closeMessage($user, $label, $reason);
            $this->systemMessages->record($locked, $user, $body);
            $locked->save();

            if ($outcome === 'deal_closed') {
                PropertyRequest::query()
                    ->whereKey($locked->request_id)
                    ->update(['status' => PropertyRequestStatus::Fulfilled->value]);

                foreach ([$locked->requester_user_id, $locked->proposer_user_id] as $userId) {
                    ProfessionalProfileCounter::increment($userId, 'total_closed_deals');
                }
            }
        });

        return $this->conversationPayloadResponse($conversation);
    }

    private function ensureConversationActionable(Conversation $conversation): void
    {
        $status = $conversation->status instanceof ConversationStatus
            ? $conversation->status
            : ConversationStatus::from((string) $conversation->status);

        if (in_array($status, [ConversationStatus::DealClosed, ConversationStatus::DealCancelled], true)) {
            abort(422, 'Cette conversation est terminée.');
        }
    }

    private function conversationPayloadResponse(Conversation $conversation): JsonResponse
    {
        $conversation->refresh()->load([
            'messages' => static fn ($q) => $q->orderBy('id'),
        ]);

        return response()->json([
            'data' => [
                'conversation' => (new ConversationResource($conversation))->resolve(),
                'messages' => MessageResource::collection($conversation->messages)->resolve(),
            ],
        ]);
    }

    private function visitStatusFrenchLabel(ConversationVisitStatus $status): string
    {
        return match ($status) {
            ConversationVisitStatus::None => 'visite non programmée',
            ConversationVisitStatus::Pending => 'visite en attente',
            ConversationVisitStatus::Scheduled => 'visite programmée',
            ConversationVisitStatus::Completed => 'visite effectuée',
            ConversationVisitStatus::Cancelled => 'visite annulée',
        };
    }

    private function commissionStatusFrenchLabel(ConversationCommissionStatus $status): string
    {
        return match ($status) {
            ConversationCommissionStatus::None => 'commission non définie',
            ConversationCommissionStatus::Proposed => 'commission proposée',
            ConversationCommissionStatus::Accepted => 'commission acceptée',
            ConversationCommissionStatus::Rejected => 'commission refusée',
        };
    }

    private function offerStatusFrenchLabel(ConversationOfferStatus $status): string
    {
        return match ($status) {
            ConversationOfferStatus::None => 'offre non envoyée',
            ConversationOfferStatus::Sent => 'offre envoyée',
            ConversationOfferStatus::Accepted => 'offre acceptée',
            ConversationOfferStatus::Rejected => 'offre refusée',
        };
    }
}
