<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Inside\Content\Contracts\WysiwygImageService;
use Inside\Content\Models\Content;
use Inside\Content\Models\Contents\Rops;
use Inside\Content\Models\Contents\Sops;
use Inside\Content\Models\Sections\Text;
use Inside\Content\Models\WysiwygImage;
use Inside\Host\Migrations\CreateContentTypes;
use Inside\Support\Str;

final class FixWysiwygImages extends Migration
{
    use CreateContentTypes;

    /**
     * @var array|string[]
     */
    protected array $types = ['text', 'rops', 'sops'];

    protected WysiwygImageService $wysiwygImageService;

    public function __construct()
    {
        $this->wysiwygImageService = App::make(WysiwygImageService::class);
        $this->console = $this->getConsole();
    }

    private function getBrokenContents(Collection $contents, bool $isSection): Collection
    {
        $broken = collect();
        $this->console->writeln('getting broken WW images contents ...');
        $contents->each(function ($content) use (&$broken, $isSection) {
            $model = $isSection ? $content->sectionable : $content;
            $langcode = $model->langcode === 'fr' ? 'en' : 'fr';
            $translation = $model->getTranslationIfExists($langcode);
            if ($translation->langcode === $model->langcode ||
                $translation->images()->count() === $model->images()->count()) {
                return;
            }
            $brokenContent = $model->images()->count() < $translation->images()->count() ? $model : $translation;
            if ($broken->contains($brokenContent)) {
                // we already identified this translation as broken
                return;
            }
            $broken->push($brokenContent);
        });
        $this->console->writeln(__(
            '=> <info>:count broken contents found</info>',
            [
                'count' => $broken->count(),
            ]
        ));

        return $broken;
    }

    private function getAllContents(string $type): Collection
    {
        return match ($type) {
            'sops' => Sops::where('goal', 'LIKE', '%inside-image%')->orWhere('standard', 'LIKE', '%inside-image%')->get(),
            'rops' => Rops::where('goal', 'LIKE', '%inside-image%')->orWhere('standard', 'LIKE', '%inside-image%')->get(),
            default => Text::where('body', 'LIKE', '%inside-image%')->get()
        };
    }

    private function fixWysiwygFile(?string $field, Content $content): array
    {
        if (! $field || ! Str::contains($field, 'inside-image')) {
            return [];
        }
        $storage = Storage::disk('local');
        $urls = $content->images ? $content->images->pluck('relative_url')->toArray() : [];
        $replacement = [];
        preg_match_all('/(?<=class="inside-image" src=")[^"]+(?=")/', $field, $matches);
        foreach ($matches[0] as $url) {
            $pathinfo = pathinfo($url);
            if (! $pathinfo['basename']) {
                throw new Exception('basename not found');
            }
            // we will find the ww images, because it exist in one language and not the other
            /** @var WysiwygImage $image */
            $image = WysiwygImage::where('filename', $pathinfo['basename'])->firstOrFail();
            $path = Str::after($image->path, env('APP_STORAGE_PATH').'/');
            $file = new UploadedFile(
                $storage->path($path),
                $pathinfo['basename'],
                $storage->mimeType($path)
            );
            $upload = $this->wysiwygImageService->upload($file);
            $urls[] = $upload;
            $replacement[$url] = $upload;
        }
        $this->wysiwygImageService->store($content, $urls);

        return $replacement;
    }

    private function handleBrokenContents(string $type, Collection $brokenContent): void
    {
        foreach ($brokenContent as $content) {
            try {
                if ($type === 'text') {
                    $this->console->write(__(
                        '=> <info>Repairing paragraphs of broken content <fg=cyan>:title (:uuid)</> of type :type</info>',
                        [
                            'title' => $content->title ?? 'not set',
                            'uuid' => $content->uuid ?? 'not set',
                            'type' => $content->content_type ?? 'unknown type',
                        ]
                    ));
                    $content->textSectionItem->each(function (Text $paragraph) use ($content) {
                        $paragraphChanges = $this->fixWysiwygFile($paragraph->body, $content);
                        $paragraph->update(['body' => strtr($paragraph->body, $paragraphChanges)]);
                    });
                } else {
                    $this->console->write(__(
                        '=> <info>Repairing content <fg=cyan>:title (:uuid)</> of type :type</info>',
                        [
                            'title' => $content->title ?? 'not set',
                            'uuid' => $content->uuid ?? 'not set',
                            'type' => $type,
                        ]
                    ));
                    $goalChanges = $this->fixWysiwygFile($content->goal, $content);
                    $standardChanges = $this->fixWysiwygFile($content->standard, $content);
                    $content->update([
                        'goal' => strtr($content->goal, $goalChanges),
                        'standard' => strtr($content->standard, $standardChanges),
                    ]);
                }
            } catch (Exception $exception) {
                $message = sprintf(
                    'Image recuperation failed %s <%s> with message %s',
                    $type, $content->uuid ?? 'null', $exception->getMessage()
                );
                Log::warning($message);
                $this->writeResult(false);
                continue;
            }
            $this->writeResult(true);
        }
    }

    public function up(): void
    {
        $brokenContents = [];
        $this->console->writeln('starting retrieval of contents ...');
        foreach ($this->types as $type) {
            $contents = collect();
            $this->console->write(__(
                '<module>retrieving contents for type :type</module>',
                [
                    'type' => $type,
                ]
            ));
            $contents = $this->getAllContents($type);
            $this->writeResult(true);
            $brokenContents[$type] = $this->getBrokenContents($contents, $type === 'text');
        }

        foreach ($brokenContents as $type => $brokenContent) {
            $this->handleBrokenContents($type, $brokenContent);
        }
    }
}
