<?php

namespace App\Http\Controllers;

use App\Models\HrRequest;
use App\Models\HrSetting;
use App\Models\User;
use App\Support\CrmModuleManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;

class HrWebhookController extends Controller
{
    public function handle(Request $request, CrmModuleManager $moduleManager): JsonResponse
    {
        $settings = HrSetting::query()->first();
        if (! $settings || ! $settings->is_active) {
            return response()->json(['status' => 'ignored'], 202);
        }

        $secret = (string) ($settings->webhook_secret ?? '');
        if ($secret !== '') {
            $incomingSecret = (string) ($request->header('X-CRM-Webhook-Secret', $request->input('secret', '')));
            if ($incomingSecret === '' || ! hash_equals($secret, $incomingSecret)) {
                return response()->json(['message' => 'Unauthorized'], 401);
            }
        }

        $payload = is_array($request->all()) ? $request->all() : [];
        $normalized = $this->normalizePayload($payload, $settings);

        $existing = null;
        if (! empty($normalized['external_id'])) {
            $existing = HrRequest::query()
                ->where('external_id', (string) $normalized['external_id'])
                ->first();
        }

        if ($existing) {
            $normalized = $moduleManager->applyPayloadHooks('hr.requests.update', $normalized, [
                'hook' => 'hr.requests.update',
                'source' => 'webhook',
                'hr_request_id' => $existing->id,
            ], array_keys($normalized));

            $existing->fill($normalized)->save();

            return response()->json(['status' => 'updated', 'id' => $existing->id]);
        }

        $normalized = $moduleManager->applyPayloadHooks('hr.requests.store', $normalized, [
            'hook' => 'hr.requests.store',
            'source' => 'webhook',
        ], array_keys($normalized));

        $hrRequest = HrRequest::query()->create($normalized);

        return response()->json(['status' => 'created', 'id' => $hrRequest->id]);
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>
     */
    private function normalizePayload(array $payload, HrSetting $settings): array
    {
        $type = strtolower(trim((string) ($payload['type'] ?? 'other')));
        if (! in_array($type, ['vacation', 'sick_leave', 'business_trip', 'reimbursement', 'onboarding', 'offboarding', 'other'], true)) {
            $type = 'other';
        }

        $status = strtolower(trim((string) ($payload['status'] ?? 'new')));
        if (! in_array($status, ['new', 'in_progress', 'waiting_approval', 'resolved', 'rejected'], true)) {
            $status = 'new';
        }

        $priority = strtolower(trim((string) ($payload['priority'] ?? 'medium')));
        if (! in_array($priority, ['low', 'medium', 'high', 'urgent'], true)) {
            $priority = 'medium';
        }

        $employeeUserId = $payload['employee_user_id'] ?? $payload['user_id'] ?? $payload['employee_id'] ?? null;
        if (! $employeeUserId && isset($payload['user_email'])) {
            $employeeUserId = User::query()->where('email', (string) $payload['user_email'])->value('id');
        }
        if (! $employeeUserId && ($settings->auto_assign_user_id ?? null)) {
            $employeeUserId = (int) $settings->auto_assign_user_id;
        }

        $assigneeUserId = $payload['assignee_user_id'] ?? $payload['assignee_id'] ?? $payload['manager_id'] ?? $settings->auto_assign_user_id;

        $requestedDate = $payload['requested_date'] ?? $payload['date'] ?? null;
        if (! $requestedDate) {
            $requestedDate = now()->toDateString();
        }

        $dueDate = $payload['due_date'] ?? $payload['deadline'] ?? null;
        if (! $dueDate && ($settings->default_sla_days ?? 0) > 0) {
            $dueDate = now()->addDays((int) $settings->default_sla_days)->toDateString();
        }

        $resolvedDate = $payload['resolved_date'] ?? $payload['completed_at'] ?? null;
        if ($status === 'resolved' && ! $resolvedDate) {
            $resolvedDate = now()->toDateString();
        }
        if ($status !== 'resolved') {
            $resolvedDate = null;
        }

        return [
            'external_id' => $this->nullableText($payload['external_id'] ?? $payload['id'] ?? $payload['request_id'] ?? null),
            'type' => $type,
            'status' => $status,
            'priority' => $priority,
            'title' => $this->nullableText($payload['title'] ?? $payload['subject'] ?? null) ?: 'HR request',
            'description' => $this->nullableText($payload['description'] ?? $payload['body'] ?? null),
            'requested_date' => $requestedDate,
            'due_date' => $dueDate,
            'resolved_date' => $resolvedDate,
            'employee_user_id' => $employeeUserId ? (int) $employeeUserId : null,
            'assignee_user_id' => $assigneeUserId ? (int) $assigneeUserId : null,
            'creator_user_id' => $payload['creator_user_id'] ?? ($employeeUserId ? (int) $employeeUserId : null),
            'meta' => $this->filterMeta($payload),
        ];
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>|null
     */
    private function filterMeta(array $payload): ?array
    {
        $meta = Arr::except($payload, ['secret', 'token', 'api_key', 'api_secret', 'password']);
        if ($meta === []) {
            return null;
        }

        return $meta;
    }

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

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