<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\TelephonyCallResource;
use App\Models\TelephonyCall;
use App\Support\AccessControl;
use App\Support\CrmModuleManager;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class TelephonyCallController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(TelephonyCall::class, 'call');
    }

    public function index(Request $request)
    {
        $search = trim((string) $request->input('q', ''));
        $direction = trim((string) $request->input('direction', ''));
        $status = trim((string) $request->input('status', ''));
        $dateFrom = trim((string) $request->input('date_from', ''));
        $dateTo = trim((string) $request->input('date_to', ''));
        $userId = $request->integer('user_id');

        $query = TelephonyCall::query()
            ->with('user:id,name,email')
            ->when($search !== '', function ($builder) use ($search): void {
                $builder->where(function ($sub) use ($search): void {
                    $sub->where('from_number', 'like', "%{$search}%")
                        ->orWhere('to_number', 'like', "%{$search}%")
                        ->orWhere('external_id', 'like', "%{$search}%");
                });
            })
            ->when(in_array($direction, $this->directionValues(), true), fn ($builder) => $builder->where('direction', $direction))
            ->when(in_array($status, $this->statusValues(), true), fn ($builder) => $builder->where('status', $status))
            ->when($dateFrom !== '', fn ($builder) => $builder->whereDate('started_at', '>=', $dateFrom))
            ->when($dateTo !== '', fn ($builder) => $builder->whereDate('started_at', '<=', $dateTo));

        if (! AccessControl::isElevated($request->user())) {
            $query->where('user_id', $request->user()->id);
        } elseif ($userId > 0) {
            $query->where('user_id', $userId);
        }

        $calls = $query
            ->orderByDesc('started_at')
            ->orderByDesc('id')
            ->paginate(30)
            ->withQueryString();

        return TelephonyCallResource::collection($calls);
    }

    public function store(Request $request, CrmModuleManager $moduleManager): TelephonyCallResource
    {
        $payload = $this->validatedData($request);
        $payload = $moduleManager->applyPayloadHooks('telephony.store', $payload, [
            'hook' => 'telephony.store',
            'user_id' => $request->user()->id,
            'source' => 'api',
        ], array_keys($payload));

        $call = TelephonyCall::query()->create($payload);

        return TelephonyCallResource::make($call->load('user'));
    }

    public function show(TelephonyCall $call): TelephonyCallResource
    {
        return TelephonyCallResource::make($call->load('user'));
    }

    public function update(Request $request, TelephonyCall $call, CrmModuleManager $moduleManager): TelephonyCallResource
    {
        $payload = $this->validatedData($request, $call);
        $payload = $moduleManager->applyPayloadHooks('telephony.update', $payload, [
            'hook' => 'telephony.update',
            'user_id' => $request->user()->id,
            'call_id' => $call->id,
            'source' => 'api',
        ], array_keys($payload));

        $call->update($payload);

        return TelephonyCallResource::make($call->load('user'));
    }

    public function destroy(TelephonyCall $call)
    {
        $call->delete();

        return response()->noContent();
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedData(Request $request, ?TelephonyCall $call = null): array
    {
        $validated = $request->validate([
            'external_id' => [
                'nullable',
                'string',
                'max:255',
                Rule::unique('telephony_calls', 'external_id')->ignore($call?->id),
            ],
            'direction' => ['required', Rule::in($this->directionValues())],
            'from_number' => ['nullable', 'string', 'max:60'],
            'to_number' => ['nullable', 'string', 'max:60'],
            'status' => ['required', Rule::in($this->statusValues())],
            'duration_seconds' => ['nullable', 'integer', 'min:0'],
            'started_at' => ['nullable', 'date'],
            'ended_at' => ['nullable', 'date'],
            'recording_url' => ['nullable', 'string', 'max:255'],
            'user_id' => ['nullable', 'exists:users,id'],
            'meta' => ['nullable', 'array'],
        ]);

        return [
            'external_id' => $this->nullableText($validated['external_id'] ?? null),
            'direction' => (string) $validated['direction'],
            'from_number' => $this->nullableText($validated['from_number'] ?? null),
            'to_number' => $this->nullableText($validated['to_number'] ?? null),
            'status' => (string) $validated['status'],
            'duration_seconds' => (int) ($validated['duration_seconds'] ?? 0),
            'started_at' => $validated['started_at'] ?? null,
            'ended_at' => $validated['ended_at'] ?? null,
            'recording_url' => $this->nullableText($validated['recording_url'] ?? null),
            'user_id' => $validated['user_id'] ?? null,
            'meta' => $validated['meta'] ?? null,
        ];
    }

    /**
     * @return list<string>
     */
    private function directionValues(): array
    {
        return ['inbound', 'outbound'];
    }

    /**
     * @return list<string>
     */
    private function statusValues(): array
    {
        return ['new', 'ringing', 'in_progress', 'completed', 'missed', 'failed'];
    }

    private function nullableText(mixed $value): ?string
    {
        $value = trim((string) $value);

        return $value === '' ? null : $value;
    }
}
