<?php

namespace App\Http\Controllers\Backoffice;

use App\Facades\Backoffice\BackendUserService;
use App\Facades\Backoffice\BackofficeService;
use App\Facades\Backoffice\ContentTypes;
use App\Facades\Backoffice\RequestContext;
use App\Http\Controllers\Controller;
use App\Http\Requests\Backoffice\AdminContentAutoComplete;
use App\Http\Requests\Backoffice\ContentTypeBulkRequest;
use App\Http\Requests\Backoffice\ContentTypeListRequest;
use App\Http\Resources\Backoffice\OrderResource;
use App\Models\Backoffice\MediaItem;
use App\Models\Backoffice\WebsiteObject;
use App\Models\Backoffice\WebsiteObjectLanguage;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Str;

class ContentController extends Controller
{
    public function getList($type)
    {
        $contentType = ContentTypes::find($type);

        return Inertia::render('Backoffice/ContentTypes/ListContent', [
            'data' => $contentType->toArray(true),
        ]);
    }

    public function postList($type, ContentTypeListRequest $request)
    {
        $contentType = ContentTypes::find($type);

        $data = $request->validated();
        $sort = $data['sort'];
/*
        if (empty($sort) && $contentType->isOrderable() && !$contentType->isHierarchic()) {
            $sort = array("key" => "title", "order" => "asc");
        }
*/
        $filter = $data['filter'];
        $list = $contentType->toList($data['page'], $data['itemsPerPage'], $sort, $data['search'], $filter);

        return response()->json($list);
    }

    public function getForm($type, $id = false, $parentId = false, $parentContentType = false, $parentFieldName = false, $isPreview = FALSE)
    {
        session(null)->forget("_old_input");

        $contentType = ContentTypes::find($type);

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        $parentData = $contentType->getParent();

        if ($parentContentType && $parentFieldName) {
            $parentType = ContentTypes::find($parentContentType);
            $parentField = $parentType->getFields()->find($parentFieldName);
            if (!$parentField || $parentField->getObjectType() != $type) {
                abort(404, trans("response.not_found"));
            }

            $parentData = array(
                'field' => $parentFieldName,
                'contentType' => $parentContentType
            );
        }

        $modelType = $contentType->getModelType();

        if (!empty($id)) {
            $model = $modelType::find($id);

            if (is_null($model)) {
                abort(404, trans("response.not_found"));
            }

        } else {
            if (!$contentType->userHasPermission()) {
                abort(403, trans("response.forbidden"));
            }

            $modelType = $contentType->getModelType();
            $model = new $modelType;
        }
        if (!$model->exists && (!empty($parentId) && empty($parentData)) ||
            (empty($parentId) && !empty($parentData))) {
            abort(400, trans("response.bad_syntax"));
        }
        RequestContext::set('generic_form_timestamp', time());

        /*
        if (request(null)->ajax()) {
            return view('admin.generic.components.form',
                array(
                    "model" => $model,
                    "contentType" => $contentType,
                    "parentId" => $parentId,
                    "isPreview" => $isPreview,
                ));
        } else {
            return view('admin.generic.pages.form',
                array(
                    "model" => $model,
                    "contentType" => $contentType,
                    "parentId" => $parentId,
                    "isPreview" => $isPreview,
                ));
        }
        */
       // dd($contentType->getData($model));
        if (request(null)->input('ajaxMode')) {
            return [
                'data' => $contentType->getData($model),
                'id' => $id ? $model->id : null,
                'message' => Session::get('message'),
                'errorMessage' => Session::get('errorMessage')
            ];
        } else {
            return Inertia::render('Backoffice/ContentTypes/EditContent', [
                'data' => $contentType->getData($model),
                'id' => $id ? $model->id : null,
                'message' => Session::get('message'),
                'errorMessage' => Session::get('errorMessage')
            ]);
        }
    }

    public function getPreview($type, $id = FALSE, $parentId = FALSE, $parentContentType = FALSE, $parentFieldName = FALSE, $isPreview = TRUE)
    {
        return $this->getForm($type, $id, $parentId, $parentContentType, $parentFieldName, $isPreview);
    }

    public function postCheckSlug()
    {
        $originSlug = request(null)->input("slug");
        $langId = request(null)->input("lang");

        if (empty($originSlug) || empty($langId)) {
            return response()->json(array(
                "error" => "bad_syntax",
                "description" => "The request is missing the slug"
            ), 400);
        }
        $slug = BackofficeService::generateSlug($originSlug, $langId);

        if ($slug === false) {
            return response()->json(array(
                "error" => "invalid",
                "description" => "Impossible to generate this slug"
            ), 400);
        } else {
            return response()->json(array("slug" => $slug));
        }
    }

    public function postForm(
        $type,
        $id = false,
        $parentId = false,
        $parentContentType = false,
        $parentFieldName = false
    ) {
        $redirectTo = request(null)->input('redirect_to') !== '' ? request(null)->input('redirect_to') : null;
        $contentType = ContentTypes::find($type);
        $languages = LaravelLocalization::getSupportedLocales();
        $isAjax = !!request(null)->input('isAjax');

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        $loggedUser = BackendUserService::getLoggedUser();
        if (!empty($id)) {
            $modelType = $contentType->getModelType();
            $model = $modelType::find($id);

            if (is_null($model)) {
                abort(404, trans("response.not_found"));
            }
        } else {
            $modelType = $contentType->getModelType();
            $model = new $modelType;
            $filters = $contentType->getFilters();
            foreach ($filters as $filter) {
                $model->$filter["field"] = $filter["value"];
            }
        }

        $parentData = $contentType->getParent();
        $hasPivot = false;

        if ($parentContentType && $parentFieldName) {
            $parentType = ContentTypes::find($parentContentType);
            $parentField = $parentType->getFields()->find($parentFieldName);
            if (!$parentField || $parentField->getObjectType() != $type) {
                abort(404, trans("response.not_found"));
            }

            $parentData = array(
                'field' => $parentFieldName,
                'contentType' => $parentContentType
            );
            $hasPivot = true;
        }

        if (!$model->exists && (!empty($parentId) && empty($parentData)) ||
            (empty($parentId) && !empty($parentData))) {
            abort(400, trans("response.bad_syntax"));
        }

        //TODO use Form Request?
        $validation = $contentType->getValidation($model);

        $validator = Validator::make(
            $validation["values"],
            $validation["rules"]
        );

        $validator->setAttributeNames($validation["labels"]);

        if ($contentType->isPage()) {
            foreach ($languages as $langCode => $lang) {
                $originSlug = $validation["values"]["slug"][$langCode];
                if (!$originSlug) {
                    $originSlug = Str::slug($validation["values"]["title"][$langCode]);
                    $validation["values"]["slug"][$langCode] = $originSlug;
                }
                $websiteObjectLang = null;
                if ($model->exists && $model->website_object_id) {
                    $websiteObjectLang = WebsiteObjectLanguage::where('website_object_id', $model->website_object_id)
                        ->where('wo_language_id', $langCode)
                        ->first();
                    if ($websiteObjectLang && $websiteObjectLang->slug == $originSlug) {
                        continue;
                    }
                }

                $slug = BackofficeService::generateSlug($originSlug, $langCode, $contentType, $websiteObjectLang);
                $validation["values"]["slug"][$langCode] = ($slug ? $slug : '');
            }
        }

        $validationFails = $validator->fails();
        $errors = $validator->errors();

        $mediaPickerErrors = [];

        // Eeters here if every rule passes
        if (!$validationFails) {
            // get Content type fields
            $contentTypeFields = $contentType->getFields();
            $mediaPickerFields = $contentTypeFields->filter(function ($value) {
                // get all mediaPicker fields in this ContentType
                return $value->getType() === 'mediaPicker' && $value->getAllowed();
            });
            foreach ($mediaPickerFields as $field) {
                // get the allowed extensions from the mediaPicker field
                $allowedExtensions = explode(',', $field->getAllowed());
                // get the ids of the media items
                $ids = json_decode(request(null)->input($field->getName()));
                // get the Media Item models
                $mediaItems = MediaItem::WhereIn('id', $ids)->get();
                foreach ($mediaItems as $mediaItem) {
                    // if the current media item extension is not in the allowed extensions
                    if (!in_array($mediaItem->extension, $allowedExtensions)) {
                        // manually add the error to the validator MessageBag
                        $errors->add($field->getName(), 'This extension is not allowed');
                        // set validationFails to true so it displays the messageBag errors
                        $validationFails = true;
                    }
                }
            }
        }

        if ($validationFails) {
            if ($isAjax) {
                return response()->json([
                    'errors' => $errors,
                ], 403);
            } else {
                throw new ValidationException($validator);
            }
        }

        BackofficeService::saveContentType(
            $contentType,
            $model,
            $validation["values"],
            $loggedUser,
            $parentData,
            $parentId,
            $hasPivot
        );

        $redirectUrl = request()->path();
        if ($id === false) {
            $redirectUrl .= "/".$model->id;
        }
        session()->forget("_old_input");

        if ($isAjax) {
            return response()->json(array(
                'id' => $model->id,
                'title' => $model->title,
                'object_id' => $model->website_object_id,
                'data' => $contentType->getData($model),
            ));
        }
        if ($redirectTo) {
            return redirect()->to($redirectTo)->withMessage(trans("backend.save_success"));
        } else {
            return redirect()->to($redirectUrl)->withMessage(trans("backend.save_success"));
        }
        /*
        if (request()->ajax()) {
            return response()->json(array(
                "id" => $model->id,
                "title" => $model->title,
                "website_object_id" => $model->website_object_id
            ));
        } else {
            //If has template and has template configuration, jumps to it

            if ($contentType->hasTemplate()) {
                $model->load('websites');
                $websiteObject = $model->websites->first();
                $objectTemplate = $contentType->getTemplates()[$websiteObject->template];
                if (isset($objectTemplate['contentType'])) {
                    $contentType = ContentTypes::find($objectTemplate['contentType']);
                    $parentData = $contentType->getParent();
                    if (empty($parentData)) {
                        abort(404, trans("response.not_found"));
                    }
                    $modelType = $contentType->getModelType();
                    $configModel = $modelType::where($parentData["field"], $model->id)->first();

                    if (!$configModel) {
                        $redirectUrl = 'admin/content/form-template/'.$objectTemplate['contentType'].'/'.$model->id;
                        return redirect()->to($redirectUrl);
                    }
                }
            }
            if ($redirectTo) {
                return redirect()->to($redirectTo)->withMessage(trans("backend.save_success"));
            } else {
                return redirect()->to($redirectUrl)->withMessage(trans("backend.save_success"));
            }
        }*/
    }

    public function postAutoCompleteModel(AdminContentAutoComplete $request, $contentType)
    {
        $data = $request->validated();
        $searchTerm = urldecode($data['searchTerm']);
        $exactId = !empty($data['id']) ? $data['id'] : null;

        $defaultLang = LaravelLocalization::getDefaultLocale();

        $contentTypeConfig = config('backoffice.contentTypes.' . $contentType);

        if (!$contentTypeConfig) {
            return response()->json(['error' => 'content type not found'], 400);
        }
        $model = $contentTypeConfig['model'];
        $contentModel = new $model;
        $langTable = $contentModel->languages()->getRelated()->getTable();

        $fk = $langTable . '.' . $contentType . '_id';

        $query = DB::table('website_objects')
            ->selectRaw("$fk as id, title, website_objects.website_object_id as object_id, website_objects.content_type as type, website_objects_lang.status as status")
            ->join('website_objects_lang', 'website_objects_lang.website_object_id', 'website_objects.website_object_id')
            ->where('website_objects_lang.wo_language_id', $defaultLang)
            ->where("content_type", $contentType);

        if ($exactId) {
            $query->join($contentModel->getTable(), 'id', 'website_objects.object_id')
                ->where('language_id', $defaultLang)
                ->where('id', $exactId);

            $query->join($langTable, $fk, 'website_objects.object_id')
                ->where('language_id', $defaultLang)
                ->where('id', $exactId);
        } else {
            $query->join($contentModel->getTable(), 'id', 'website_objects.object_id')
                ->where('language_id', $defaultLang)
                ->where('title', 'like', "%" . $searchTerm . "%");

            $query->join($langTable, $fk, 'website_objects.object_id')
                ->where('language_id', $defaultLang)
                ->where('title', 'like', "%" . $searchTerm . "%");
        }

        $results = $query->limit(10)->get();

        return response()->json($results, 200);
    }

    public function getObjectTitles(int $objectId)
    {
        $websiteObject = WebsiteObject::find($objectId);

        if (!$websiteObject) {
            return response()->json(['error' => 'Website object not found'], 404);
        }

        $object = $websiteObject->objectable;
        $out = [];
        $objectTranslations = $object->languages->keyBy('language_id')->toArray();

        $websiteObject->languages()->get()->each(function ($woLang) use ($objectTranslations, &$out) {
            $out[$woLang->wo_language_id] = [
                'title' => $objectTranslations[$woLang->wo_language_id]['title'],
                'uri' => $woLang->uri,
            ];
        });

        return response()->json($out);
    }

    public function postAutoComplete(AdminContentAutoComplete $request, $baseContentype = null, $field = null)
    {
        $contentField = 'title';

        if ($baseContentype) {
            $contentTypeConfig = config('backoffice.contentTypes.' . $baseContentype);

            if (!$contentTypeConfig) {
                return response()->json(['error' => 'content type not found'], 400);
            }

            $fieldConfig = array_filter($contentTypeConfig['fields'], function($fieldConfig) use ($field) {
                return $fieldConfig['name'] === $field;
            });

            $fieldConfig = array_pop($fieldConfig);

            if ($fieldConfig['type'] == 'selectBox' && !empty($fieldConfig['contentType'])) {
                $objectTypes = [$fieldConfig['contentType']];

                if (!empty($fieldConfig['contentField'])) {
                    $contentField = $fieldConfig['contentField'];
                }
            } elseif (!empty($fieldConfig['objectTypes'])) {
                $objectTypes = $fieldConfig['objectTypes'];
            } else {
                return response()->json(['error' => 'field or object types config not found'], 400);
            }
        } else {
            $objectTypes = ContentTypes::pageTypes();
        }

        $data = $request->validated();
        $searchTerm = urldecode($data['searchTerm']);
        $exactId = !empty($data['id']) ? $data['id'] : null;
        $exactObjectIds = !empty($data['objectIds']) ? $data['objectIds'] : null;

        if (!empty($data['objectType']) && in_array($data['objectType'], $objectTypes)) {
            $objectTypes = [$data['objectType']];
        }

        $defaultLang = LaravelLocalization::getDefaultLocale();
        $mergedResults = [];

        foreach ($objectTypes as $objectType) {
            $typeLocation = $objectType == 'event_item' ? 'event' : $objectType;
            $contentType = config('backoffice.contentTypes.' . $typeLocation);
            $model = $contentType['model'];
            $contentModel = new $model;
            $langTable = $contentModel->languages()->getRelated()->getTable();

            $fk = $langTable . '.' . $objectType . '_id';

            $query = DB::table('website_objects')
                ->selectRaw("$fk as id, website_objects.website_object_id as object_id, website_objects.content_type as type, website_objects_lang.status as status")
                ->join('website_objects_lang', 'website_objects_lang.website_object_id', 'website_objects.website_object_id')
                ->where('website_objects_lang.wo_language_id', $defaultLang)
                ->where("content_type", $contentType['type']);

            if ($exactId || $exactObjectIds) {
                $query->join($contentModel->getTable(), 'id', 'website_objects.object_id')
                    ->addSelect($contentField . ' as title')
                    ->where('language_id', $defaultLang);

                $query->join($langTable, $fk, 'website_objects.object_id');

                if ($exactId) {
                    $query->where($contentModel->getTable() . '.id', $exactId);
                } elseif ($exactObjectIds) {
                    $query->whereIn('website_objects.website_object_id', $exactObjectIds);
                }
            } else {
                $query->join($contentModel->getTable(), 'id', 'website_objects.object_id')
                    ->join($langTable, $fk, 'website_objects.object_id')
                    ->where('language_id', $defaultLang)
                    ->where(function ($query) use ($searchTerm, $langTable, $contentType, $contentModel) {
                        foreach ($contentType['fields'] as $searchField) {
                            if ($searchField['search'] ?? false) {
                                if ($searchField['multiLanguage'] ?? false) {
                                    $query->orWhere($langTable . '.' . $searchField['name'], 'like', "%" . $searchTerm . "%");
                                } else {
                                    $query->orWhere($contentModel->getTable() . '.' . $searchField['name'], 'like', "%" . $searchTerm . "%");
                                }
                            }
                        }
                    });
            }
            foreach ($contentType['fields'] as $searchField) {
                // add seach fields to the query
                if ($searchField['search'] ?? false) {
                    $query->addSelect($searchField['name'] . ' as ' . $searchField['name']);
                }
            }

            $results = $query->limit(sizeof($objectTypes) === 1 ? 19 : 5)->get();

            $mergedResults = array_merge($mergedResults, $results->toArray());
        }

        return response()->json($mergedResults, 200);
    }

    public function deleteContentType($type, $id)
    {
        $contentType = ContentTypes::find($type);

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        try {
            BackofficeService::deleteContentType($contentType, $id);
        } catch (NotFoundHttpException $e) {
            return response()->json(["success" => true]);
        }

        return response()->json(["success" => true]);
    }

    public function getOrder($type)
    {
        $contentType = ContentTypes::find($type);

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        if ($contentType->isHierarchic()) {
            $entries = $contentType->toStructuredList();
        } else {
            $entries = $contentType->toList(null, null, array('position' => 'asc'))->all();
        }

        return Inertia::render('Backoffice/ContentTypes/ListContentOrder', [
            'data' => new OrderResource($entries),
            'contentType' => [
                'type' => $contentType->getType(),
                'label' => $contentType->getLabel(),
                'isHierarchic' => $contentType->isHierarchic()
            ]
        ]);
    }

    public function postOrder($type)
    {
        $contentType = ContentTypes::find($type);

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        $newOrder = request(null)->input('newOrder');
        $newOrder = json_decode($newOrder);

        if (!$newOrder || !is_array($newOrder)) {
            abort(400, trans("response.bad_syntax"));
        }

        try {
            BackofficeService::reorderContentType($contentType, $newOrder);
        } catch (BadRequestHttpException $e) {
            abort(400, trans("response.bad_syntax"));
        }

        return response()->json(array(
            'message' => 'success',
        ));
    }

    public function getFormTemplate($type, $parentId)
    {

        $contentType = ContentTypes::find($type);

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        $parentData = $contentType->getParent();

        if (empty($parentData)) {
            abort(404, trans("response.not_found"));
        }
        $modelType = $contentType->getModelType();
        $model = $modelType::where($parentData["field"], $parentId)->first();

        if (empty($model)) {
            $loggedUser = BackendUserService::getLoggedUser();
            $model = BackofficeService::createContentTypeTemplate($contentType, $parentId, $loggedUser);
        }
        $url = "/admin/content/form/{$type}/{$model->id}/{$parentId}";


        return response()->redirectTo($url);
    }

    public function postBulk($type, ContentTypeBulkRequest $request)
    {
        $contentType = ContentTypes::find($type);

        if (!$contentType->userHasPermission()) {
            abort(403, trans("response.forbidden"));
        }

        $data = $request->validated();
        $ids = $data['ids'];
        $action = $data['action'];
        $errors = [];

        foreach ($ids as $id) {
            try {
                if ($action === 'delete') {
                    BackofficeService::deleteContentType($contentType, $id);
                } else {
                    BackofficeService::updateContentTypeStatus($contentType, $id, $action);
                }
            } catch (\Exception $e) {
                $errors[] = "ID {$id}: " . $e->getMessage();
            }
        }

        return response()->json(["success" => true, "errors" => $errors]);
    }
}
