<?php

namespace onespace\tools\activeApi\helpers;

use Exception;
use onespace\tools\enum\general\HTTPMethod;
use Yii;
use yii\web\HttpException;
use yii\web\NotFoundHttpException;
use yii\web\Response;

trait RestControllerHelperTrait {
    /**
     * Allows the user to quickly return custom formatted error response with
     * the selected response code and custom message.
     *
     * @param   int             $code       The HTTP response code, should never be 200 in this context.
     * @param   string|array    $message    The custom message to return to the requester.
     *
     * @return  yii\web\Response
     *
     * @throws  Exception   If parsed code is 200
     *
     * @access  protected
     */
    protected function customErrorResponse(int $code, string|array $message): Response {
        if ($code == 200) {
            throw new Exception("Error code should never be 200.");
        }
        Yii::$app->response->statusCode = $code;
        Yii::$app->response->format = Response::FORMAT_JSON;
        Yii::$app->response->data = [
            'success' => false,
            'code' => $code,
            'name' => Response::$httpStatuses[$code] ?? 'Unknown',
            'message' => $message,
        ];
        return Yii::$app->response;
    }

    /**
     * Finds the specified model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param   string  $modelObject    The full object classname, derived from `MyModel::class`
     * @param   integer $id
     * @return \yii\db\ActiveRecord the loaded model
     * @throws HttpException if the model cannot be found
     *
     * @access  protected
     */
    protected function findModel(string $modelObject, int $id): \yii\db\ActiveRecord {
        if (($model = $modelObject::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }

    /**
     * Returns the various searching properties, `$params`, `$sort`, `$offset`, `$limit`. Returned
     * as an array.
     *
     * @param   \onespace\tools\helpers\enum\HTTPMethod  $method Default: HTTPMethod::GET
     *
     * @return  array
     *
     * @access  protected
     */
    protected function getRequestProperties(HTTPMethod $method = HTTPMethod::GET): array {
        if ($method === HTTPMethod::GET) {
            $params = Yii::$app->request->get('params', null);
            $sort = Yii::$app->request->get('sort', null);
            $offset = Yii::$app->request->get('offset', 0);
            $limit = Yii::$app->request->get('limit', 0);
            $with = Yii::$app->request->get('with', null);
        } else {
            $params = Yii::$app->request->post('params', null);
            $sort = Yii::$app->request->post('sort', null);
            $offset = Yii::$app->request->post('offset', 0);
            $limit = Yii::$app->request->post('limit', 0);
            $with = Yii::$app->request->post('with', null);
        }

        return [
            $params,
            $sort,
            $offset,
            $limit,
            $with,
        ];
    }

    /**
     * Apply any offsets and limits to an SQL ActiveQuery.
     *
     * @param   \yii\db\ActiveQuery &$query The query to have a limit and offset applied.
     * @param   int $limit  The limit applied to the query. If 0, this will be skipped.
     * @param   int $offset The offset applied to the query. If 0, this will be skipped.
     *
     * @access  protected
     */
    protected function applyLimitAndOffset(\yii\db\ActiveQuery &$query, int $limit, int $offset): void {
        if ($limit > 0) {
            $query = $query->limit($limit);
        }
        if ($offset > 0) {
            $query = $query->offset($offset);
        }
    }

    /**
     * Apply any sorting to the SQL ActiveQuery.
     *
     * @param   \yii\db\ActiveQuery &$query     The query to have the sorting.
     * @param   string|null         $sort       The sort param parsed from the query.
     * @param   array|null          $default    The default sorting routing. Default: null
     *
     * @access  protected
     */
    protected function applySort(\yii\db\ActiveQuery &$query, ?string $sort, ?array $default = null): void {
        if ($sort === null) {
            if ($default !== null) {
                $query = $query->orderBy($default);
            }
        } else {
            if (substr($sort, 0, 1) === '-') {
                $sort = trim($sort, '-');
                $query = $query->orderBy([$sort => SORT_DESC]);
            } else {
                $query = $query->orderBy([$sort => SORT_ASC]);
            }
        }
    }

    /**
     * Apply any basic where filtering to the query.
     *
     * @param   \yii\db\ActiveQuery &$query     The query to have the sorting.
     * @param   array|null          $where      Properties to add to the query.
     *
     * @access  protected
     */
    protected function applyWhere(\yii\db\ActiveQuery &$query, ?array $where): void {
        if ($where !== null) {
            $andFilters = [];
            foreach ($where as $i => $param) {
                Yii::debug("Key: " . $i, __METHOD__);
                Yii::debug($param, __METHOD__);
                if (is_array($param) && isset($param['0'])) {
                    switch (strtoupper($param['0'])) {
                        case '>':
                        case '>=':
                        case '<':
                        case '<=':
                        case '!=':
                        case 'BETWEEN':
                        case 'LIKE':
                            $andFilters[] = $param;
                            unset($where[$i]);
                            break;
                    }
                } elseif (is_string($param)) {
                    switch (strtoupper($param)) {
                        case 'IS NULL':
                            $andFilters[] = ['IS', $i, new \yii\db\Expression('NULL')];
                            unset($where[$i]);
                            break;
                        case 'IS NOT NULL':
                            $andFilters[] = ['IS NOT', $i, new \yii\db\Expression('NULL')];
                            unset($where[$i]);
                            break;
                    }
                }
            }

            $query = $query->where($where);

            foreach ($andFilters as $filter) {
                Yii::debug($filter, __METHOD__);
                $query = $query->andFilterWhere($filter);
            }
        }
    }

    /**
     * Apply any existing table relationships to do cross-table querying.
     *
     * Note that this does not allow for returning data from other tables, only querying via joins.
     * Due to this, join types are not yet implemented (as they are redundant).
     *
     * @param   \yii\db\ActiveQuery &$query     The query to have the sorting.
     * @param   array|null          $with       Table relationship aliases to add to the query.
     *
     */
    protected function applyWith(\yii\db\ActiveQuery &$query, ?array $with): void {
        if ($with === null) {
            return;
        }

        $query = $query->joinWith($with);
    }
}
