<?php

namespace App\Traits;

use App\Core\Module\ManageableModuleManager;
use App\Models\Module;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

/**
 * PRC-398 / PRC-327 — boilerplate index/edit/update/toggle/test handlers
 * for `Admin\<Family>ModuleController`s. Concrete controller declares the
 * 5 abstract hooks; this trait handles permissioning, validation,
 * persistence, activity logging, and AJAX response shape.
 *
 * Caller responsibilities:
 *  - `moduleType()` — string key persisted in `modules.type` (sms, email, …)
 *  - `manager()` — `ManageableModuleManager` instance (discover/fields/test)
 *  - `permission()` — Spatie permission name (`manage sms modules`, …)
 *  - `indexView()` / `editView()` — Blade view names
 *
 * Concrete controller MUST `use LogsAdminActivity` (audit row written by
 * `logUpdated` after every mutation).
 */
trait ManagesModules
{
    abstract protected function moduleType(): string;

    abstract protected function manager(): ManageableModuleManager;

    abstract protected function permission(): string;

    abstract protected function indexView(): string;

    abstract protected function editView(): string;

    public function index(): View
    {
        $this->authorize($this->permission());

        return view($this->indexView(), [
            'type' => $this->moduleType(),
            'modules' => Module::ofType($this->moduleType())
                ->orderBy('sort_order')
                ->orderBy('label')
                ->get(),
        ]);
    }

    public function edit(Module $module): View
    {
        $this->authorize($this->permission());
        abort_unless($module->type === $this->moduleType(), 404);

        $fields = $this->manager()->getConfigFields($module->name);

        return view($this->editView(), [
            'module' => $module->load('settings'),
            'fields' => $fields,
            'config' => $module->config,
        ]);
    }

    public function update(Request $request, Module $module): RedirectResponse
    {
        $this->authorize($this->permission());
        abort_unless($module->type === $this->moduleType(), 404);

        $fields = $this->manager()->getConfigFields($module->name);

        // Build validation rules dynamically from the field schema; each
        // field declares its own rule string/array (`rules`).
        $rules = collect($fields)
            ->mapWithKeys(fn (array $f) => [
                $f['key'] => $f['rules'] ?? 'nullable|string|max:1024',
            ])
            ->all();

        $data = $request->validate($rules);

        // PRC-623 — boolean fields: normalize to '1'/'0'. The edit form posts
        // a hidden "0" alongside the checkbox, but keep a server-side safety
        // belt so an absent/garbage value still persists an explicit FALSE
        // instead of silently keeping the previous value.
        foreach ($fields as $field) {
            if (($field['type'] ?? null) === 'boolean') {
                $data[$field['key']] = $request->boolean($field['key']) ? '1' : '0';
            }
        }

        $encryptedKeys = collect($fields)
            ->filter(fn (array $f) => ($f['encrypted'] ?? false) === true)
            ->pluck('key')
            ->all();

        DB::transaction(function () use ($module, $data, $encryptedKeys) {
            $module->setConfig($data, $encryptedKeys);

            // PRC-191 — log BEFORE commit per CLAUDE.md activity-logging contract.
            if (method_exists($this, 'logUpdated')) {
                $this->logUpdated(
                    model: $module,
                    description: "Updated {$module->type} module {$module->name}"
                );
            }
        });

        return redirect()
            ->back()
            ->with('success', __('admin/modules.updated'));
    }

    public function toggle(Module $module): JsonResponse
    {
        $this->authorize($this->permission());
        abort_unless($module->type === $this->moduleType(), 404);

        $module->update(['enabled' => ! $module->enabled]);

        if (method_exists($this, 'logUpdated')) {
            $this->logUpdated(
                model: $module,
                description: "Toggled {$module->type} module {$module->name} to "
                    . ($module->enabled ? 'enabled' : 'disabled')
            );
        }

        return response()->json([
            'enabled' => $module->enabled,
            'message' => $module->enabled
                ? __('admin/modules.toggle_enabled')
                : __('admin/modules.toggle_disabled'),
        ]);
    }

    public function test(Module $module): JsonResponse
    {
        $this->authorize($this->permission());
        abort_unless($module->type === $this->moduleType(), 404);

        $result = $this->manager()->test($module->name, $module->config);
        $module->markTested($result->success, $result->message);

        return response()->json([
            'success' => $result->success,
            'message' => $result->message,
            'context' => $result->context,
            'tested_at' => $module->fresh()->last_test_at?->toIso8601String(),
        ]);
    }
}
