<?php

namespace App\Http\Controllers\Api;

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

class OneCExchangeController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(OneCExchange::class, 'exchange');
    }

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

        $query = OneCExchange::query()
            ->with('initiatedBy:id,name,email')
            ->when($search !== '', function ($builder) use ($search): void {
                $builder->where(function ($sub) use ($search): void {
                    $sub->where('external_id', 'like', "%{$search}%")
                        ->orWhere('error_message', 'like', "%{$search}%")
                        ->orWhere('payload', 'like', "%{$search}%")
                        ->orWhere('response', 'like', "%{$search}%");
                });
            })
            ->when(in_array($status, $this->statusValues(), true), fn ($builder) => $builder->where('status', $status))
            ->when(in_array($direction, $this->directionValues(), true), fn ($builder) => $builder->where('direction', $direction))
            ->when(in_array($entity, $this->entityValues(), true), fn ($builder) => $builder->where('entity', $entity))
            ->when($dateFrom !== '', fn ($builder) => $builder->whereDate('created_at', '>=', $dateFrom))
            ->when($dateTo !== '', fn ($builder) => $builder->whereDate('created_at', '<=', $dateTo));

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

        $exchanges = $query
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->paginate(30)
            ->withQueryString();

        return OneCExchangeResource::collection($exchanges);
    }

    public function store(Request $request, CrmModuleManager $moduleManager): OneCExchangeResource
    {
        $payload = $this->validatedData($request);
        $this->ensureRequestedOwner($request, $payload);

        if (! ($payload['initiated_by_user_id'] ?? null)) {
            $payload['initiated_by_user_id'] = $request->user()->id;
        }

        $payload = $moduleManager->applyPayloadHooks('onec.exchanges.store', $payload, [
            'hook' => 'onec.exchanges.store',
            'source' => 'api',
            'user_id' => $request->user()->id,
        ], array_keys($payload));

        $exchange = OneCExchange::query()->create($payload);

        return OneCExchangeResource::make($exchange->load('initiatedBy'));
    }

    public function show(OneCExchange $exchange): OneCExchangeResource
    {
        return OneCExchangeResource::make($exchange->load('initiatedBy'));
    }

    public function update(Request $request, OneCExchange $exchange, CrmModuleManager $moduleManager): OneCExchangeResource
    {
        $payload = $this->validatedData($request, $exchange);
        $this->ensureRequestedOwner($request, $payload);

        $payload = $moduleManager->applyPayloadHooks('onec.exchanges.update', $payload, [
            'hook' => 'onec.exchanges.update',
            'source' => 'api',
            'user_id' => $request->user()->id,
            'exchange_id' => $exchange->id,
        ], array_keys($payload));

        $exchange->update($payload);

        return OneCExchangeResource::make($exchange->fresh()->load('initiatedBy'));
    }

    public function retry(Request $request, OneCExchange $exchange): OneCExchangeResource
    {
        $this->authorize('update', $exchange);

        $exchange->forceFill([
            'status' => 'queued',
            'error_message' => null,
            'started_at' => null,
            'finished_at' => null,
        ])->save();

        return OneCExchangeResource::make($exchange->fresh()->load('initiatedBy'));
    }

    public function destroy(OneCExchange $exchange)
    {
        $exchange->delete();

        return response()->noContent();
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedData(Request $request, ?OneCExchange $exchange = null): array
    {
        $directionInput = strtolower(trim((string) $request->input('direction', $exchange?->direction ?? 'outbound')));

        $validated = $request->validate([
            'external_id' => [
                'nullable',
                'string',
                'max:255',
                Rule::unique('one_c_exchanges', 'external_id')
                    ->where(fn ($query) => $query->where('direction', $directionInput))
                    ->ignore($exchange?->id),
            ],
            'direction' => ['required', Rule::in($this->directionValues())],
            'entity' => ['required', Rule::in($this->entityValues())],
            'action' => ['required', Rule::in($this->actionValues())],
            'status' => ['required', Rule::in($this->statusValues())],
            'payload' => ['nullable', 'array'],
            'response' => ['nullable', 'array'],
            'error_message' => ['nullable', 'string', 'max:5000'],
            'started_at' => ['nullable', 'date'],
            'finished_at' => ['nullable', 'date'],
            'initiated_by_user_id' => ['nullable', 'integer', 'exists:users,id'],
            'meta' => ['nullable', 'array'],
        ]);

        return [
            'external_id' => $this->nullableText($validated['external_id'] ?? null),
            'direction' => (string) $validated['direction'],
            'entity' => (string) $validated['entity'],
            'action' => (string) $validated['action'],
            'status' => (string) $validated['status'],
            'payload' => $validated['payload'] ?? null,
            'response' => $validated['response'] ?? null,
            'error_message' => $this->nullableText($validated['error_message'] ?? null),
            'started_at' => $validated['started_at'] ?? null,
            'finished_at' => $validated['finished_at'] ?? null,
            'initiated_by_user_id' => $validated['initiated_by_user_id'] ?? null,
            'meta' => $validated['meta'] ?? null,
        ];
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function ensureRequestedOwner(Request $request, array $payload): void
    {
        $targetUserId = (int) ($payload['initiated_by_user_id'] ?? 0);
        if ($targetUserId <= 0) {
            return;
        }

        if (AccessControl::isElevated($request->user())) {
            return;
        }

        abort_if($targetUserId !== (int) $request->user()->id, 403);
    }

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

    /**
     * @return list<string>
     */
    private function statusValues(): array
    {
        return ['queued', 'processing', 'completed', 'failed', 'skipped'];
    }

    /**
     * @return list<string>
     */
    private function entityValues(): array
    {
        return ['tasks', 'deals', 'companies', 'contacts', 'products', 'warehouses'];
    }

    /**
     * @return list<string>
     */
    private function actionValues(): array
    {
        return ['upsert', 'create', 'update', 'delete'];
    }

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

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

