<?php

namespace App\Http\Controllers;

use App\Models\TelephonyCall;
use App\Models\TelephonySetting;
use App\Models\User;
use App\Support\AccessControl;
use App\Support\CrmModuleManager;
use App\Support\SectionAccessManager;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class TelephonyController extends Controller
{
    public function index(Request $request, SectionAccessManager $sectionAccessManager): View
    {
        $this->authorize('viewAny', TelephonyCall::class);

        $user = $request->user();
        $isElevated = AccessControl::isElevated($user);

        $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', ''));
        $selectedUserId = $isElevated ? $request->integer('user_id') : (int) $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, array_keys($this->directionOptions()), true), fn ($builder) => $builder->where('direction', $direction))
            ->when(in_array($status, array_keys($this->statusOptions()), 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 (! $isElevated) {
            $query->where('user_id', $user->id);
        } elseif ($selectedUserId > 0) {
            $query->where('user_id', $selectedUserId);
        }

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

        $settings = TelephonySetting::query()->first();
        if (! $settings) {
            $settings = new TelephonySetting([
                'provider' => 'manual',
                'is_active' => false,
                'webhook_secret' => Str::random(40),
            ]);
        } elseif (! $settings->webhook_secret) {
            $settings->webhook_secret = Str::random(40);
        }

        $canManageSettings = AccessControl::allows($user, 'telephony', 'update');
        $canCreateCall = AccessControl::allows($user, 'telephony', 'create');

        $canManageSectionAccess = $sectionAccessManager->canManage($user, 'telephony');
        $sectionAccessUsers = $canManageSectionAccess
            ? User::query()->orderBy('name')->get(['id', 'name', 'email', 'role', 'permissions'])
            : collect();

        $userOptions = $isElevated
            ? User::query()->orderBy('name')->get(['id', 'name', 'email', 'job_title'])
            : User::query()->whereKey($user->id)->get(['id', 'name', 'email', 'job_title']);

        return view('telephony.index', [
            'calls' => $calls,
            'settings' => $settings,
            'search' => $search,
            'direction' => $direction,
            'status' => $status,
            'dateFrom' => $dateFrom,
            'dateTo' => $dateTo,
            'selectedUserId' => $selectedUserId,
            'directionOptions' => $this->directionOptions(),
            'statusOptions' => $this->statusOptions(),
            'userOptions' => $userOptions,
            'canManageSettings' => $canManageSettings,
            'canCreateCall' => $canCreateCall,
            'canManageSectionAccess' => $canManageSectionAccess,
            'sectionAccessUsers' => $sectionAccessUsers,
            'webhookUrl' => route('telephony.webhook'),
        ]);
    }

    public function updateSettings(Request $request): RedirectResponse
    {
        $user = $request->user();
        abort_unless(AccessControl::allows($user, 'telephony', 'update'), 403);

        $validated = $request->validate([
            'provider' => ['required', 'string', 'max:60'],
            'is_active' => ['nullable', 'boolean'],
            'api_base_url' => ['nullable', 'string', 'max:255'],
            'api_key' => ['nullable', 'string', 'max:255'],
            'api_secret' => ['nullable', 'string', 'max:255'],
            'account_id' => ['nullable', 'string', 'max:255'],
            'inbound_number' => ['nullable', 'string', 'max:60'],
            'outbound_number' => ['nullable', 'string', 'max:60'],
            'webhook_secret' => ['nullable', 'string', 'max:255'],
        ]);

        $payload = [
            'provider' => trim((string) $validated['provider']),
            'is_active' => $request->boolean('is_active'),
            'api_base_url' => $this->nullableText($validated['api_base_url'] ?? null),
            'api_key' => $this->nullableText($validated['api_key'] ?? null),
            'api_secret' => $this->nullableText($validated['api_secret'] ?? null),
            'account_id' => $this->nullableText($validated['account_id'] ?? null),
            'inbound_number' => $this->nullableText($validated['inbound_number'] ?? null),
            'outbound_number' => $this->nullableText($validated['outbound_number'] ?? null),
            'webhook_secret' => $this->nullableText($validated['webhook_secret'] ?? null),
        ];

        if (($payload['webhook_secret'] ?? '') === '') {
            $payload['webhook_secret'] = TelephonySetting::query()->value('webhook_secret') ?: Str::random(40);
        }

        $settings = TelephonySetting::query()->first() ?? new TelephonySetting();
        $settings->fill($payload)->save();

        return back()->with('success', __('Telephony settings have been updated.'));
    }

    public function storeCall(Request $request, CrmModuleManager $moduleManager): RedirectResponse
    {
        $this->authorize('create', TelephonyCall::class);

        $payload = $this->validatedCall($request);
        $payload = $moduleManager->applyPayloadHooks('telephony.store', $payload, [
            'hook' => 'telephony.store',
            'user_id' => $request->user()->id,
            'source' => 'web',
        ], array_keys($payload));

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

        return redirect()
            ->route('telephony.index')
            ->with('success', __('Call has been added.'));
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedCall(Request $request): array
    {
        $validated = $request->validate([
            'external_id' => ['nullable', 'string', 'max:255', Rule::unique('telephony_calls', 'external_id')],
            'direction' => ['required', Rule::in(array_keys($this->directionOptions()))],
            'from_number' => ['nullable', 'string', 'max:60'],
            'to_number' => ['nullable', 'string', 'max:60'],
            'status' => ['required', Rule::in(array_keys($this->statusOptions()))],
            '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'],
        ]);

        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,
        ];
    }

    /**
     * @return array<string, string>
     */
    private function directionOptions(): array
    {
        return [
            'inbound' => __('Inbound'),
            'outbound' => __('Outbound'),
        ];
    }

    /**
     * @return array<string, string>
     */
    private function statusOptions(): array
    {
        return [
            'new' => __('New'),
            'ringing' => __('Ringing'),
            'in_progress' => __('In progress'),
            'completed' => __('Completed'),
            'missed' => __('Missed'),
            'failed' => __('Failed'),
        ];
    }

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

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