<?php

namespace Inside\BCLH\Http\Middlewares;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Lang;
use Inside\Authentication\Models\User;
use Inside\Content\Models\Contents\Brands;
use Inside\Content\Models\Contents\Users;
use Inside\Notify\Models\Notification;
use Inside\Permission\Facades\Permission;
use Inside\Slug\Models\Slug;
use Inside\Slug\Services\SlugService;
use Symfony\Component\HttpFoundation\Response;

/**
 * Custom slugs for BCLH contents
 *
 * @category Class
 * @author   Maecia <technique@maecia.com>
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
 * @link     http://www.maecia.com/
 */
class BCLHMiddleware
{
    /**
     * @var array<int, string>
     */
    protected array $contentTypes = [
        'procedures',
        'sops',
        'rops',
        'documents',
        'declined_documents',
        'folders',
        'photo',
        'pages',
    ];

    public function handle(Request $request, Closure $next): mixed
    {
        $currentPath = $request->path();

        $paths = [
            'api/v1/content',
            'api/v1/search',
            'api/v1/special/search',
            'api/v1/users/reactions',
            'api/v1/notifications',
        ];

        $applyCustomSlug = false;
        $isSearch = false;
        $isSpecialSearch = false;
        $isSave = false;
        $isNotifications = false;

        foreach ($paths as $path) {
            if (str_starts_with($currentPath, $path)) {
                switch ($path) {
                    case 'api/v1/search':
                        $isSearch = true;
                        break;
                    case 'api/v1/special/search':
                        $isSpecialSearch = true;
                        break;
                    case 'api/v1/notifications':
                        $isNotifications = true;
                        break;
                    case 'api/v1/content':
                        if ($currentPath === 'api/v1/content/asset') {
                            return $next($request);
                        }

                        if ($currentPath === 'api/v1/content/wysiwyg/images') {
                            return $next($request);
                        }

                        if ($request->is('api/v1/content/*/export')) {
                            return $next($request);
                        }

                        if ($request->getMethod() !== 'GET') {
                            $isSave = true;
                        }
                        break;
                }

                $applyCustomSlug = true;
                break;
            }
        }

        if (! $applyCustomSlug) {
            return $next($request);
        }

        if (! $isSearch && ! $isSpecialSearch && $request->has('query')) {
            $filters = json_decode($request->get('filters'), true);
            $filters['title:like'] = '%'.$request->get('query').'%';
            if (isset($filters['sops'])) {
                unset($filters['sops']);
            }
            $request['filters'] = json_encode($filters);
        }

        $response = $next($request);
        if ($response instanceof Response && $response->getStatusCode() != 200) {
            return $response;
        }
        /** @var array $content */
        $content = json_decode_response($response);

        if (! $isSave && empty($content['data'])) {
            return $response;
        }

        if ($isSearch && array_key_exists('current_page', $content)) {
            $isSearch = false;
        }

        if ($isSearch) {
            $content = ['data' => $content['data'][0]];
        }

        if ($isSpecialSearch) {
            $original = $content;

            // Change special search format to Bclh middleware one
            $content = ['data' => []];
            foreach ($original['data'] as $uuid => $item) {
                $item['content_type'] = $item['type'];
                $item['uuid'] = $uuid;
                $content['data'][] = $item;
            }
        }

        if ($isSave) {
            $content = ['data' => [$content]];
        }

        foreach ($content['data'] as &$data) {
            if ($isSave) {
                $data['content_type'] = str_replace([
                    'api/v1/content/',
                    '/',
                    $data['uuid'],
                ], [
                    '',
                    '',
                    '',
                ], $currentPath);
            }

            if (isset($data['content_type']) && in_array($data['content_type'], $this->contentTypes)) {
                $this->handleContent($data, $request->getMethod());
            }

            if (isset($data['content_type']) && $data['content_type'] === 'folders') {
                /** @var string|null $currentBrand */
                $currentBrand = $request->has('currentBrand') ? $request->get('currentBrand') : null;
                $this->handleGedRestrictions($data, $currentBrand);

                if ($currentBrand) {
                    $this->handleBrandFilter($data, $currentBrand);
                }
            }

            $linkFields = ['link', 'sop_block_link', 'brand_block_link'];

            foreach ($linkFields as $linkField) {
                if (array_key_exists($linkField, $data)) {
                    $this->handleLinks($data, $linkField);
                }
            }

            if ($isNotifications) {
                $this->handleNotifications($data);
            }
        }

        if ($isSpecialSearch) {
            $cleaned = $content;
            $content = $original;
            $content['data'] = [];
            foreach ($cleaned['data'] as $item) {
                $content['data'][$item['uuid']] = $item;
                $content['data'][$item['uuid']]['url'] = $content['data'][$item['uuid']]['custom_slug'];
            }
        }

        if ($isSave) {
            $content = $content['data']['0'];
        }

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

    protected function handleBrandFilter(array &$data, ?string $currentBrand): void
    {
        $contentTypes = ['sops', 'documents', 'rops', 'declined_documents', 'procedures'];

        if (array_key_exists('is_media_folder', $data) && $data['is_media_folder']) {
            $contentTypes[] = 'folders';
        }

        $brandUuids = [$currentBrand];

        $brandSource = Brands::find($currentBrand);

        $translations = [];

        if ($brandSource instanceof Brands) {
            $translations = DB::table('inside_content_brands')
                ->where('uuid_host', $brandSource->uuid_host)
                ->where('langcode', '<>', $brandSource->langcode)
                ->get();
        }

        foreach ($translations as $translation) {
            $brandUuids[] = $translation->uuid;
        }

        foreach ($contentTypes as $contentType) {
            if (! array_key_exists($contentType, $data)) {
                continue;
            }

            if (empty($data[$contentType]['data'])) {
                continue;
            }

            $contents = [];

            foreach ($data[$contentType]['data'] as $content) {
                if (! array_key_exists('brands', $content)) {
                    continue;
                }

                if (isset($content['brands']['data']) && empty($content['brands']['data'])) {
                    continue;
                }

                $brands = [];

                foreach ($content['brands']['data'] as $brand) {
                    $brands[] = $brand['uuid'];
                }

                $hasCurrentBrand = false;

                foreach ($brandUuids as $brandUuid) {
                    if (in_array($brandUuid, $brands)) {
                        $hasCurrentBrand = true;
                        break;
                    }
                }

                if (! $hasCurrentBrand) {
                    continue;
                }

                $contents[] = $content;
            }

            $data[$contentType]['data'] = $contents;
        }
    }

    protected function handleNotifications(array &$data): void
    {
        /** @var Notification $notification */
        $notification = Notification::query()->find($data['id']);
        $notification = $notification->toArray();

        if (array_key_exists('author', $data)) {
            unset($data['author']);
        }

        if (str_contains($data['content'], '%contentType%')) {
            $data['content'] = str_replace('%contentType%', Lang::getFromJson('notifications.contentType.'.class_to_type($notification['notifiable_type'])), $data['content']);
        }

        if ($notification['notifiable_type'] && $notification['notifiable_uuid']) {
            $query = call_user_func($notification['notifiable_type'].'::withoutGlobalScopes');
            $model = $query->where('uuid', $notification['notifiable_uuid'])->first();

            if ($model) {
                if (in_array($data['text'], ['notifications.workflow.proposals.edited.text', 'notifications.workflow.proposals.accepted.text', 'notifications.workflow.proposals.declined.text'])) {
                    $from = Users::find($model->author);
                    $data['content'] = str_replace('%author%', trim($from['firstname'].' '.$from['lastname']), $data['content']);
                }

                $contentType = str_replace('inside_content_', '', $model->getTable());
                if (! in_array($data['text'], ['notifications.workflow.proposals.accepted.text', 'notifications.ropCreated.text'])) {
                    $data['url'] = 'edit/'.$contentType.'/'.$notification['notifiable_uuid'];
                }

                if ($data['text'] == 'notifications.ropCreated.text') {
                    if ($data['text'] === 'notifications.ropCreated.text') {
                        $model = $model->sops->first();
                    } else {
                        $model = $model->documents->first();
                    }

                    $data['model'] = ['title' => $model->title];
                    $data['content'] = str_replace('%model.title%', $data['model']['title'], $data['content']);
                }
            }
        }
    }

    protected function handleGedRestrictions(array &$data, ?string $currentBrand = null): void
    {
        $user = Auth::user();
        $userPermission = Permission::user($user);
        $customPerms = Permission::customList($userPermission);
        $countries = $user?->countries->pluck('title');

        $exceptRoles = [
            'super_administrator',
            'administrator',
            'webmaster',
            'Siège',
            'Business Unit',
            'Régions',
            'Operating Manager',
        ];

        if (array_key_exists('sops', $data)) {
            if (isset($data['sops']['data']) && ! empty($data['sops']['data'])) {
                $this->handleBrandFilter($data['sops']['data'], $currentBrand);

                foreach ($data['sops']['data'] as $key => $sop) {
                    $this->handleBrandFilter($data['sops']['data'][$key], $currentBrand);
                    if (isset($sop['rops']['data']) && ! empty($sop['rops']['data'])) {
                        if ($user->roles->pluck('name')->intersect($exceptRoles)->isEmpty()) {
                            $concern = collect($sop['rops']['data'])->pluck('countries.data')->flatten();
                            if ($countries->diff($concern)->isEmpty()) {
                                foreach ($sop['rops']['data'] as $ropKey => $rop) {
                                    $sop['rops']['data'][$ropKey]['sops'] = ['data' => []];
                                }

                                $data['rops']['data'] = array_merge($data['rops']['data'], $sop['rops']['data']);
                                unset($data['sops']['data'][$key]);
                            }
                        }
                    }
                }
            }
        }

        if (array_key_exists('procedures', $data)) {
            if (isset($data['procedures']['data']) && ! empty($data['procedures']['data'])) {
                $this->handleBrandFilter($data['procedures']['data'], $currentBrand);
            }
        }

        $data['declined_documents']['data'] = [];
        if (array_key_exists('documents', $data)) {
            if (isset($data['documents']['data']) && ! empty($data['documents']['data'])) {
                $this->handleBrandFilter($data['documents']['data'], $currentBrand);
                foreach ($data['documents']['data'] as $key => $document) {
                    $this->handleBrandFilter($data['documents']['data'][$key], $currentBrand);

                    if (isset($document['declined_documents']['data']) && ! empty($document['declined_documents']['data'])) {
                        if (! in_array('see all documents', $customPerms)) {
                            $data['declined_documents']['data'] = array_merge($data['declined_documents']['data'], $document['declined_documents']['data']);
                            unset($data['documents']['data'][$key]);
                        }
                    }
                }
            }
        }

        if (isset($data['rops']['data']) && ! empty($data['rops']['data'])) {
            foreach ($data['rops']['data'] as $key => $rop) {
                if (isset($rop['sops']['data']) && ! empty($rop['sops']['data'])) {
                    unset($data['rops']['data'][$key]);
                }
            }
        }
    }

    protected function handleContent(array &$data, string $method): void
    {
        $contentType = $data['content_type'];
        $contentUuid = $data['uuid'];

        $this->applySlug($data, $contentType, $contentUuid);

        if ($method !== 'DELETE') {
            $data['custom_slug'] .= sprintf('&modal/%s/%s', $contentType, $contentUuid);
        }

        $contentTypes = ['rops', 'declined_documents'];

        foreach ($contentTypes as $childContentType) {
            if (array_key_exists($childContentType, $data) && ! empty($data[$childContentType]) && array_key_exists('data', $data[$childContentType]) && ! empty($data[$childContentType]['data'])) {
                foreach ($data[$childContentType]['data'] as &$child) {
                    $this->handleContent($child, $method);
                }
            }
        }
    }

    /**
     * @param string $value
     * @return array
     * @deprecated
     */
    protected function transformLink(string $value): array
    {
        $url = $value;
        /** @var array $info */
        $info = parse_url($url);
        $external = (isset($info['host']) && $info['scheme'].'://'.$info['host'] !== env('APP_URL'));
        $fragment = '';

        if (isset($info['fragment'])) {
            if (($fragment = trim($info['fragment'])) != '') {
                $fragment = '#'.$fragment;
            }
        }

        $value = [
            'url' => ! $external && isset($info['path']) ? ($info['path'].$fragment) : $url,
            'uuid' => '',
            'type' => '',
            'external' => $external,
        ];
        $slug = Slug::where('slug', '=', $url)->first();

        // TODO check if this is still used by the front
        if ($slug) {
            $value['type'] = str_replace('inside_content_', '', $slug->type);
            $value['uuid'] = $slug->uuid;

            $content = call_user_func(type_to_class($value['type']).'::find', $value['uuid']);
            /** @var User $user */
            $user = Auth::user();

            if ($content && $content->langcode !== $user->langcode) {
                $content = $content->getTranslationIfExists($user->langcode);
                $value['uuid'] = $content->uuid;
            }
        } else {
            if ($url == 'home') {
                // Special case for home
                $value['url'] = '';
            }

            if (isset($info['scheme']) && in_array($info['scheme'], ['uuid', 'mailto'])) {
                switch ($info['scheme']) {
                    case 'uuid':
                        $url = explode(':', $value['url']);
                        $value['url'] = $url[1];
                        $value['type'] = $url[0];
                        $value['uuid'] = $url[1];
                        break;
                    case 'mailto':
                        $value['url'] = $url;
                        $value['type'] = 'mailto';
                        break;
                }
            }
        }

        return $value;
    }

    protected function handleLinks(array &$data, string $linkField = 'link'): void
    {
        if (! array_key_exists($linkField, $data) || is_null($data[$linkField])) {
            return;
        }

        if (! is_array($data[$linkField])) {
            $data[$linkField] = $this->transformLink($data[$linkField]);
        }

        if (empty($data[$linkField]) || ! array_key_exists('type', $data[$linkField])) {
            return;
        }

        $contentType = str_replace('inside_content_', '', $data[$linkField]['type']);

        $contentTypes = array_merge($this->contentTypes, ['shortcuts']);

        if (! in_array($contentType, $contentTypes)) {
            return;
        }

        $this->applySlug($data[$linkField], $contentType, $data[$linkField]['uuid']);
        $data[$linkField]['custom_slug'] .= sprintf('&modal/%s/%s', $contentType, $data[$linkField]['uuid']);

        $data[$linkField]['url'] = $data[$linkField]['custom_slug'];
    }

    protected function applySlug(array &$data, string $contentType, string $contentUuid): void
    {
        $service = new SlugService();
        $url = $service->getParentSlug($contentType, $contentUuid);

        $data['custom_slug'] = $url;
    }
}
