<?php
namespace apexl\Io\includes;

use apexl\entityCore\interfaces\entityInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class Controller
{

    public function __construct()
    {

    }

    /**
     * Generic entity data getter - single or multi record, allows for filtering (can be overridden
     * @param Request $request
     * @param Response $response
     * @param $args
     * @param entityInterface $entity
     * @param array $params
     * @return Response @see this->customFilters and paging
     */
    protected function getEntityData(Request $request, Response $response, $args, EntityInterface $entity, $params = []): Response
    {
        if(empty($params)) {
            $params = $request->getQueryParams();
        }
        $filters = $this->buildFilterConditions($entity, $params);

        if ($id = $args[$entity->primaryKey] ?? FALSE) {
            $entity->load($id);
            if (!isset($entity->id)) {
                return System::asJson($response, ['data' => []], 404);
            }
            return System::asJson($response, ['data' => $entity->getData()]);
        }
        //get our data, check if we're paging data add any filters ETC
        $entityData = $entity->loadByPage($params, $filters, $params['orderBy'] ?? []);

        //do we need this data as a table?
        $tabaliseData = isset($params['asTable']) && !empty($params['asTable']) ? $params['asTable'] : 0;
        if($tabaliseData){
            //if so, we need to change the return format.
            //@todo - im sure we can do this better
            $entityData['tableHeader'] = !empty($entityData['data']) ? array_keys(current($entityData['data'])) : [];
            $entityData['rows'] = $entityData['data'];
            unset($entityData['data']);
        }

        $entityData = Hook::processHook('getEntityData', $entityData, $entity->getEntityName());

        //no table? just return the data.
        return System::asJson($response, $entityData);
    }

    /**
     * Generic SetEntity method - used to handle normal Entity data storage, without having to duplicate code.
     * @param Request $request
     * @param Response $response
     * @param $args
     * @param entityInterface $entity
     * @param array $extraData
     * @return Response
     * @throws \Exception
     */
    protected function setEntityData(Request $request, Response $response, $args, EntityInterface $entity, $extraData = []): Response
    {
        $body = $request->getParsedBody();
        //we have an id? then this is an update, so load the entity first.
        if ($id = $args[$entity->primaryKey] ?? FALSE) {
            $entity->load($id);
            if (!isset($entity->id)) {
                return System::asJson($response, ['data' => []], 404);
            }
        }
        //we grab the table cols so we can check which data we can actually store.
        $fields = $entity->getTableColumns();
        $originalData = $entity->getData();
        $entity->setData([]); //blank the data so we dont have extra fields to store.
        foreach($fields as $field){
            $entity->{$field->Field} = $body->{$field->Field} ?? ($originalData[$field->Field] ?? $field->Default);
        }
        $output = [];
        $success = TRUE;
        if($entity->isValid()){
            $entity->store();
            //set the id into the original data
            $originalData[$entity->primaryKey] = $entity->{$entity->primaryKey};
            //restore the data into the entity.
            $entity->setData($originalData);
            $output['data'] = $entity->getData();
            $output['content'] = $extraData['message'] ?? $entity->getNiceName().' saved.';
        } else {
            $success = FALSE;
            $output['content'] = $extraData['message'] ?? 'Saving '.$entity->getNiceName().' failed. The following fields are missing: '.implode(', ', $entity->getMissingData());
        }
        return System::asJson($response, System::output($output, $extraData['route'] ?? '', $success, $extraData['method'] ?? 'redirect'));
    }

    /**
     * Function to build filter conditions
     * @param $entity
     * @param $params
     * @return array
     */
    protected function buildFilterConditions($entity, $params): array
    {
        $filters = [];
        $fields = $entity->getTableColumns();
        //make sure that
        $validFields = [];
        foreach($fields as $field){
            $validFields[] = $field->Field;
        }
        foreach($params as $param => $value){
            if(is_array($value)){
                //Skip orderBy's
                if($param == "orderBy"){
                    continue;
                }
                //assume $col 0 is the field name.
                //assume if we have a . in the params, then we're referencing a joined table.
                if(in_array($value[0], $validFields) || strpos($param, '.') !== FALSE) {
                    foreach ($value as $col) {
                        $filters[$param][] = $col;
                    }
                }
            } else {
                //assume if we have a . in the params, then we're referencing a joined table.
                if(strpos($param, '.') !== FALSE){
                    $filters[$param] = [$param, $value];
                } else {
                    if (in_array($param, $validFields)) {
                        $filters[$param] = [$param, $value];
                    }
                }
            }
        }
        return $filters;
    }

    protected function throw404(Response $response){
        return System::asJson($response, [], 404);
    }
}