<?php

declare(strict_types=1);

namespace PortalPeople\monitor\controllers\api;

use PortalPeople\monitor\services\MonitorService;
use Yii;
use yii\filters\AccessControl;
use yii\filters\auth\HttpBearerAuth;
use yii\rest\Controller;
use yii\web\ForbiddenHttpException;
use yii\web\Response;

/**
 * Monitor API Controller
 *
 * REST JSON endpoints for system health monitoring.
 * Requires Bearer token authentication and Global Admins group membership.
 *
 * Routes (registered by Module::bootstrap() for app ID 'NGaugeApi'):
 *   GET v1/monitor/all
 *   GET v1/monitor/health
 *   GET v1/monitor/queues
 *   GET v1/monitor/db
 *   GET v1/monitor/disk
 *   GET v1/monitor/errors
 *   GET v1/monitor/wrappers
 *
 * Optional query parameters (all actions):
 *   warn_waiting     int  Queue waiting threshold (default 100)
 *   warn_stalled     int  Stalled jobs threshold (default 5)
 *   warn_age_seconds int  Oldest job age threshold in seconds (default 3600)
 *   warn_disk_percent int Disk usage % warning threshold (default 80)
 *   error_minutes    int  Error log look-back window in minutes (default 60)
 */
class MonitorController extends Controller
{
    public function behaviors(): array
    {
        $behaviors = parent::behaviors();

        $behaviors['authenticator'] = [
            'class' => HttpBearerAuth::class,
        ];

        $behaviors['access'] = [
            'class' => AccessControl::class,
            'rules' => [
                [
                    'allow'         => true,
                    'matchCallback' => static function (): bool {
                        $user = Yii::$app->user->identity;
                        return $user !== null && method_exists($user, 'inGroup') && $user->inGroup('Global Admins');
                    },
                ],
            ],
            'denyCallback' => static function (): void {
                $user = Yii::$app->user;
                Yii::warning(
                    sprintf(
                        'Monitor API access denied — user: %s, IP: %s',
                        $user->isGuest ? 'guest' : (string) $user->id,
                        Yii::$app->request->userIP ?? 'unknown'
                    ),
                    'security'
                );
                throw new ForbiddenHttpException('Access restricted to Global Admins.');
            },
        ];

        return $behaviors;
    }

    public function beforeAction($action): bool
    {
        if (!parent::beforeAction($action)) {
            return false;
        }

        Yii::info(
            sprintf(
                'Monitor API action: %s — user: %s, IP: %s',
                $action->id,
                Yii::$app->user->isGuest ? 'guest' : (string) Yii::$app->user->id,
                Yii::$app->request->userIP ?? 'unknown'
            ),
            'audit'
        );

        return true;
    }

    public function verbs(): array
    {
        return [
            'all'      => ['GET'],
            'health'   => ['GET'],
            'queues'   => ['GET'],
            'db'       => ['GET'],
            'disk'     => ['GET'],
            'errors'   => ['GET'],
            'wrappers' => ['GET'],
        ];
    }

    /** Full dashboard: modules + queues + db + disk + errors */
    public function actionAll(): array
    {
        return $this->makeService()->all(
            (int) Yii::$app->request->get('error_minutes', 60)
        );
    }

    /** Module registration health check */
    public function actionHealth(): array
    {
        $modules   = $this->makeService()->modules();
        $allOk     = !in_array('unavailable', array_column($modules, 'status'), true);

        return [
            'timestamp' => date('c'),
            'status'    => $allOk ? 'healthy' : 'degraded',
            'modules'   => $modules,
        ];
    }

    /** Queue depth and stalled job counts per channel */
    public function actionQueues(): array
    {
        return [
            'timestamp' => date('c'),
            'queues'    => $this->makeService()->queues(),
        ];
    }

    /** Database connectivity and latency */
    public function actionDb(): array
    {
        return array_merge(['timestamp' => date('c')], $this->makeService()->database());
    }

    /** Disk usage for runtime / storage paths */
    public function actionDisk(): array
    {
        return [
            'timestamp' => date('c'),
            'disk'      => $this->makeService()->disk(),
        ];
    }

    /** Recent error summary from app.log */
    public function actionErrors(): array
    {
        return array_merge(
            ['timestamp' => date('c')],
            $this->makeService()->errors(
                (int) Yii::$app->request->get('error_minutes', 60)
            )
        );
    }

    /** Wrapper controller → module ID map with load status */
    public function actionWrappers(): array
    {
        $service = $this->makeService();
        $map     = $service->wrapperMap();
        $result  = [];

        foreach ($map as $wrapper => $moduleId) {
            $result[$wrapper] = [
                'module_id' => $moduleId,
                'loaded'    => Yii::$app->getModule($moduleId) !== null,
            ];
        }

        return ['timestamp' => date('c'), 'wrappers' => $result];
    }

    // -------------------------------------------------------------------------

    private function makeService(): MonitorService
    {
        $service = new MonitorService();

        $get = Yii::$app->request->get(...);

        if (($v = $get('warn_waiting'))      !== null) { $service->warnWaiting     = (int) $v; }
        if (($v = $get('warn_stalled'))      !== null) { $service->warnStalled     = (int) $v; }
        if (($v = $get('warn_age_seconds'))  !== null) { $service->warnAgeSeconds  = (int) $v; }
        if (($v = $get('warn_disk_percent')) !== null) { $service->warnDiskPercent = (int) $v; }

        return $service;
    }
}
