<?php

namespace App\Http\Controllers;

use App\Models\OneCExchange;
use App\Models\OneCSetting;
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 OneCController extends Controller
{
    public function index(Request $request, SectionAccessManager $sectionAccessManager): View
    {
        $this->authorize('viewAny', OneCExchange::class);

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

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

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

        $settings = OneCSetting::query()->first();
        if (! $settings) {
            $settings = new OneCSetting([
                'provider' => '1c',
                'is_active' => false,
                'exchange_mode' => 'bidirectional',
                'auto_sync' => false,
                'sync_tasks' => true,
                'sync_deals' => true,
                'sync_companies' => true,
                'sync_contacts' => true,
                'sync_products' => true,
                'sync_warehouses' => true,
                'sync_interval_minutes' => 15,
                'webhook_secret' => Str::random(40),
            ]);
        } elseif (! $settings->webhook_secret) {
            $settings->webhook_secret = Str::random(40);
        }

        $canManageSettings = AccessControl::allows($user, 'onec', 'update');
        $canCreateExchange = AccessControl::allows($user, 'onec', 'create');
        $canUpdateExchange = AccessControl::allows($user, 'onec', 'update');

        $canManageSectionAccess = $sectionAccessManager->canManage($user, 'onec');
        $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('onec.index', [
            'exchanges' => $exchanges,
            'settings' => $settings,
            'search' => $search,
            'status' => $status,
            'direction' => $direction,
            'entity' => $entity,
            'dateFrom' => $dateFrom,
            'dateTo' => $dateTo,
            'selectedUserId' => $selectedUserId,
            'statusOptions' => $this->statusOptions(),
            'directionOptions' => $this->directionOptions(),
            'entityOptions' => $this->entityOptions(),
            'actionOptions' => $this->actionOptions(),
            'exchangeModeOptions' => $this->exchangeModeOptions(),
            'userOptions' => $userOptions,
            'canManageSettings' => $canManageSettings,
            'canCreateExchange' => $canCreateExchange,
            'canUpdateExchange' => $canUpdateExchange,
            'canManageSectionAccess' => $canManageSectionAccess,
            'sectionAccessUsers' => $sectionAccessUsers,
            'webhookUrl' => route('onec.webhook'),
        ]);
    }

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

        $validated = $request->validate([
            'provider' => ['required', 'string', 'max:60'],
            'is_active' => ['nullable', 'boolean'],
            'endpoint_url' => ['nullable', 'string', 'max:255'],
            'username' => ['nullable', 'string', 'max:255'],
            'password' => ['nullable', 'string', 'max:255'],
            'api_key' => ['nullable', 'string', 'max:255'],
            'api_secret' => ['nullable', 'string', 'max:255'],
            'webhook_secret' => ['nullable', 'string', 'max:255'],
            'exchange_mode' => ['required', Rule::in(array_keys($this->exchangeModeOptions()))],
            'auto_sync' => ['nullable', 'boolean'],
            'sync_tasks' => ['nullable', 'boolean'],
            'sync_deals' => ['nullable', 'boolean'],
            'sync_companies' => ['nullable', 'boolean'],
            'sync_contacts' => ['nullable', 'boolean'],
            'sync_products' => ['nullable', 'boolean'],
            'sync_warehouses' => ['nullable', 'boolean'],
            'sync_interval_minutes' => ['required', 'integer', 'min:1', 'max:1440'],
        ]);

        $payload = [
            'provider' => trim((string) $validated['provider']),
            'is_active' => $request->boolean('is_active'),
            'endpoint_url' => $this->nullableText($validated['endpoint_url'] ?? null),
            'username' => $this->nullableText($validated['username'] ?? null),
            'password' => $this->nullableText($validated['password'] ?? null),
            'api_key' => $this->nullableText($validated['api_key'] ?? null),
            'api_secret' => $this->nullableText($validated['api_secret'] ?? null),
            'webhook_secret' => $this->nullableText($validated['webhook_secret'] ?? null),
            'exchange_mode' => (string) $validated['exchange_mode'],
            'auto_sync' => $request->boolean('auto_sync'),
            'sync_tasks' => $request->boolean('sync_tasks'),
            'sync_deals' => $request->boolean('sync_deals'),
            'sync_companies' => $request->boolean('sync_companies'),
            'sync_contacts' => $request->boolean('sync_contacts'),
            'sync_products' => $request->boolean('sync_products'),
            'sync_warehouses' => $request->boolean('sync_warehouses'),
            'sync_interval_minutes' => (int) $validated['sync_interval_minutes'],
        ];

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

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

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

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

        $payload = $this->validatedExchange($request);

        if (! AccessControl::isElevated($request->user())) {
            $payload['initiated_by_user_id'] = $request->user()->id;
        } elseif (! ($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' => 'web',
            'user_id' => $request->user()->id,
        ], array_keys($payload));

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

        return redirect()
            ->route('onec.index')
            ->with('success', __('Exchange entry has been created.'));
    }

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

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

        return back()->with('success', __('Exchange has been queued for retry.'));
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedExchange(Request $request): array
    {
        $directionInput = strtolower(trim((string) $request->input('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)),
            ],
            'direction' => ['required', Rule::in(array_keys($this->directionOptions()))],
            'entity' => ['required', Rule::in(array_keys($this->entityOptions()))],
            'action' => ['required', Rule::in(array_keys($this->actionOptions()))],
            'status' => ['required', Rule::in(array_keys($this->statusOptions()))],
            'payload' => ['nullable', 'json'],
            'response' => ['nullable', 'json'],
            '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', 'json'],
        ]);

        $payloadData = $this->decodeJsonArray($validated['payload'] ?? null);
        $responseData = $this->decodeJsonArray($validated['response'] ?? null);
        $metaData = $this->decodeJsonArray($validated['meta'] ?? null);

        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' => $payloadData,
            'response' => $responseData,
            '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' => $metaData,
        ];
    }

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

    /**
     * @return array<string, string>
     */
    private function statusOptions(): array
    {
        return [
            'queued' => __('Queued'),
            'processing' => __('Processing'),
            'completed' => __('Completed'),
            'failed' => __('Failed'),
            'skipped' => __('Skipped'),
        ];
    }

    /**
     * @return array<string, string>
     */
    private function entityOptions(): array
    {
        return [
            'tasks' => __('Tasks'),
            'deals' => __('Deals'),
            'companies' => __('Companies'),
            'contacts' => __('Contacts'),
            'products' => __('Products'),
            'warehouses' => __('Warehouses'),
        ];
    }

    /**
     * @return array<string, string>
     */
    private function actionOptions(): array
    {
        return [
            'upsert' => __('Upsert'),
            'create' => __('Create'),
            'update' => __('Update'),
            'delete' => __('Delete'),
        ];
    }

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

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

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

    /**
     * @return array<string, mixed>|null
     */
    private function decodeJsonArray(mixed $value): ?array
    {
        $raw = trim((string) $value);
        if ($raw === '') {
            return null;
        }

        /** @var mixed $decoded */
        $decoded = json_decode($raw, true);
        if (! is_array($decoded)) {
            return null;
        }

        return $decoded;
    }
}

