<?php

namespace App\Http\Controllers\Api;

use App\Jobs\ExecuteAiAction;
use App\Models\AiAction;
use App\Services\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Validation\ValidationException;

class AiActionController extends Controller
{
    public function index(Request $request)
    {
        $perPage = $this->resolvePerPage($request);

        $q = AiAction::query()->with(['server:id,hostname', 'approver:id,name'])
            ->when($request->filled('status') && $request->input('status') !== 'all',
                fn ($qb) => $qb->where('status', $request->input('status')))
            ->when($request->filled('server_id'),
                fn ($qb) => $qb->where('server_id', $request->input('server_id')))
            ->when($request->filled('risk') && $request->input('risk') !== 'all',
                fn ($qb) => $qb->where('risk', $request->input('risk')))
            ->orderByDesc('created_at');

        return response()->json($q->paginate($perPage));
    }

    public function show(AiAction $action)
    {
        return response()->json($action->load(['server', 'approver']));
    }

    public function approve(Request $request, AiAction $action)
    {
        abort_unless($request->user()->canApproveActions(), 403);
        if (! $action->isPending()) abort(409, 'Action is not pending.');

        // Destructive commands: require fresh TOTP re-auth.
        if ($action->isDestructive()) {
            $this->requireRecentTotp($request);
        }

        $action->update([
            'status'      => AiAction::STATUS_APPROVED,
            'approved_by' => $request->user()->id,
            'approved_at' => now(),
        ]);

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'approval.granted', 'ai_action', (string) $action->id,
            ['command' => $action->command, 'risk' => $action->risk], $request);

        // Dispatch executor — runs sync when QUEUE_CONNECTION=sync (local dev),
        // queued in production. Defaults to dry-run unless HSAP_EXECUTE_LIVE=true.
        ExecuteAiAction::dispatch($action->id);

        return response()->json($action->fresh());
    }

    public function reject(Request $request, AiAction $action)
    {
        abort_unless($request->user()->canApproveActions(), 403);
        if (! $action->isPending()) abort(409, 'Action is not pending.');

        $data = $request->validate([
            'reason' => ['required', 'string', 'max:1000'],
        ]);

        $action->update([
            'status'           => AiAction::STATUS_REJECTED,
            'approved_by'      => $request->user()->id,
            'approved_at'      => now(),
            'rejection_reason' => $data['reason'],
        ]);

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'approval.rejected', 'ai_action', (string) $action->id,
            ['reason' => $data['reason']], $request);

        return response()->json($action->fresh());
    }

    public function modify(Request $request, AiAction $action)
    {
        abort_unless($request->user()->canApproveActions(), 403);
        if (! $action->isPending()) abort(409, 'Action is not pending.');

        $data = $request->validate([
            'command' => ['required', 'string', 'max:8000'],
            'risk'    => ['nullable', 'in:low,medium,high,critical'],
        ]);

        $original = $action->command;
        $action->update([
            'command' => $data['command'],
            'risk'    => $data['risk'] ?? $action->risk,
            'status'  => AiAction::STATUS_MODIFIED,
        ]);

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'ai_action.modified', 'ai_action', (string) $action->id,
            ['from' => $original, 'to' => $data['command']], $request);

        return response()->json($action->fresh());
    }

    public function bulkApprove(Request $request)
    {
        abort_unless($request->user()->canApproveActions(), 403);

        $data = $request->validate([
            'ids' => ['required', 'array', 'min:1', 'max:50'],
            'ids.*' => ['integer'],
        ]);

        $actions = AiAction::whereIn('id', $data['ids'])
            ->where('status', AiAction::STATUS_PENDING)
            ->get();

        $approved = [];
        foreach ($actions as $a) {
            // Bulk path is for low-risk only — anything destructive must go via single approve.
            if ($a->isDestructive()) continue;
            $a->update([
                'status'      => AiAction::STATUS_APPROVED,
                'approved_by' => $request->user()->id,
                'approved_at' => now(),
            ]);
            $approved[] = $a->id;
        }

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'approval.bulk_granted', 'ai_action', null,
            ['ids' => $approved, 'requested' => $data['ids']], $request);

        return response()->json([
            'approved'   => $approved,
            'rejected'   => array_values(array_diff($data['ids'], $approved)),
        ]);
    }

    private function requireRecentTotp(Request $request): void
    {
        // Local dev — TOTP requirement disabled means we trust the session.
        if (! config('hsap.auth.require_2fa', true)) return;

        $code = $request->input('totp_code');
        if (! $code || strlen($code) !== 6) {
            throw ValidationException::withMessages([
                'totp_code' => ['Re-authentication required for destructive commands. Provide a current TOTP code.'],
            ]);
        }

        $user = $request->user();
        $g2fa = new \PragmaRX\Google2FA\Google2FA;
        if (! $user->totp_secret || ! $g2fa->verifyKey($user->totp_secret, $code, 1)) {
            throw ValidationException::withMessages([
                'totp_code' => ['Invalid authenticator code.'],
            ]);
        }
    }

    private function resolvePerPage(Request $request): int
    {
        $opts = config('hsap.pagination.options', [25, 50, 100]);
        $req  = (int) $request->input('per_page', 25);
        return in_array($req, $opts, true) ? $req : 25;
    }
}
