<?php

namespace BookStack\Exports\ZipExports;

use BookStack\Exports\ZipExports\Models\ZipExportModel;
use BookStack\Uploads\ImageService;
use Illuminate\Validation\Factory;

class ZipValidationHelper
{
    protected Factory $validationFactory;

    /**
     * Local store of validated IDs (in format "<type>:<id>". Example: "book:2")
     * which we can use to check uniqueness.
     * @var array<string, bool>
     */
    protected array $validatedIds = [];

    /**
     * Local store of ZIP file references used during validation.
     * This may contain (and is expected to support) duplicates in the event multiple items reference the same file.
     * This is used for late-stage validation for file size limits.
     * @var string[]
     */
    protected array $fileReferenceUses = [];

    public function __construct(
        public ZipExportReader $zipReader,
    ) {
        $this->validationFactory = app(Factory::class);
    }

    public function validateData(array $data, array $rules): array
    {
        $messages = $this->validationFactory->make($data, $rules)->errors()->messages();

        foreach ($messages as $key => $message) {
            $messages[$key] = implode("\n", $message);
        }

        return $messages;
    }

    public function fileReferenceRule(array $acceptedMimes = []): ZipFileReferenceRule
    {
        return new ZipFileReferenceRule($this, $acceptedMimes);
    }

    public function imageFileReferenceRule(): ZipFileReferenceRule
    {
        return new ZipFileReferenceRule($this, ImageService::getSupportedMimeTypes());
    }

    public function trackUseOfFile(string $file): void
    {
        $this->fileReferenceUses[] = $file;
    }

    public function uniqueIdRule(string $type): ZipUniqueIdRule
    {
        return new ZipUniqueIdRule($this, $type);
    }

    public function hasIdBeenUsed(string $type, mixed $id): bool
    {
        $key = $type . ':' . $id;
        if (isset($this->validatedIds[$key])) {
            return true;
        }

        $this->validatedIds[$key] = true;

        return false;
    }

    /**
     * @return string[]
     */
    public function getFileReferenceUses(): array
    {
        return $this->fileReferenceUses;
    }

    /**
     * Validate an array of relation data arrays that are expected
     * to be for the given ZipExportModel.
     * @param class-string<ZipExportModel> $model
     */
    public function validateRelations(array $relations, string $model): array
    {
        $results = [];

        foreach ($relations as $key => $relationData) {
            if (is_array($relationData)) {
                $results[$key] = $model::validate($this, $relationData);
            } else {
                $results[$key] = [trans('validation.zip_model_expected', ['type' => gettype($relationData)])];
            }
        }

        return $results;
    }
}
