<?php

namespace Kirby\Cms;

use Kirby\Filesystem\F;
use Kirby\Toolkit\Str;

/**
 * Extension of the basic blueprint class
 * to handle all blueprints for files.
 *
 * @package   Kirby Cms
 * @author    Bastian Allgeier <bastian@getkirby.com>
 * @link      https://getkirby.com
 * @copyright Bastian Allgeier GmbH
 * @license   https://getkirby.com/license
 */
class FileBlueprint extends Blueprint
{
    /**
     * `true` if the default accepted
     * types are being used
     *
     * @var bool
     */
    protected $defaultTypes = false;

    public function __construct(array $props)
    {
        parent::__construct($props);

        // normalize all available page options
        $this->props['options'] = $this->normalizeOptions(
            $this->props['options'] ?? true,
            // defaults
            [
                'changeName' => null,
                'create'     => null,
                'delete'     => null,
                'read'       => null,
                'replace'    => null,
                'update'     => null,
            ]
        );

        // normalize the accept settings
        $this->props['accept'] = $this->normalizeAccept($this->props['accept'] ?? []);
    }

    /**
     * @return array
     */
    public function accept(): array
    {
        return $this->props['accept'];
    }

    /**
     * Returns the list of all accepted MIME types for
     * file upload or `*` if all MIME types are allowed
     *
     * @return string
     */
    public function acceptMime(): string
    {
        // don't disclose the specific default types
        if ($this->defaultTypes === true) {
            return '*';
        }

        $accept       = $this->accept();
        $restrictions = [];

        if (is_array($accept['mime']) === true) {
            $restrictions[] = $accept['mime'];
        } else {
            // only fall back to the extension or type if
            // no explicit MIME types were defined
            // (allows to set custom MIME types for the frontend
            // check but still restrict the extension and/or type)

            if (is_array($accept['extension']) === true) {
                // determine the main MIME type for each extension
                $restrictions[] = array_map(['Kirby\Filesystem\Mime', 'fromExtension'], $accept['extension']);
            }

            if (is_array($accept['type']) === true) {
                // determine the MIME types of each file type
                $mimes = [];
                foreach ($accept['type'] as $type) {
                    if ($extensions = F::typeToExtensions($type)) {
                        $mimes[] = array_map(['Kirby\Filesystem\Mime', 'fromExtension'], $extensions);
                    }
                }

                $restrictions[] = array_merge(...$mimes);
            }
        }

        if ($restrictions !== []) {
            if (count($restrictions) > 1) {
                // only return the MIME types that are allowed by all restrictions
                $mimes = array_intersect(...$restrictions);
            } else {
                $mimes = $restrictions[0];
            }

            // filter out empty MIME types and duplicates
            return implode(', ', array_filter(array_unique($mimes)));
        }

        // no restrictions, accept everything
        return '*';
    }

    /**
     * @param mixed $accept
     * @return array
     */
    protected function normalizeAccept($accept = null): array
    {
        if (is_string($accept) === true) {
            $accept = [
                'mime' => $accept
            ];
        } elseif ($accept === true) {
            // explicitly no restrictions at all
            $accept = [
                'mime' => null
            ];
        } elseif (empty($accept) === true) {
            // no custom restrictions
            $accept = [];
        }

        $accept = array_change_key_case($accept);

        $defaults = [
            'extension'   => null,
            'mime'        => null,
            'maxheight'   => null,
            'maxsize'     => null,
            'maxwidth'    => null,
            'minheight'   => null,
            'minsize'     => null,
            'minwidth'    => null,
            'orientation' => null,
            'type'        => null
        ];

        // default type restriction if none are configured;
        // this ensures that no unexpected files are uploaded
        if (
            array_key_exists('mime', $accept) === false &&
            array_key_exists('extension', $accept) === false &&
            array_key_exists('type', $accept) === false
        ) {
            $defaults['type']   = ['image', 'document', 'archive', 'audio', 'video'];
            $this->defaultTypes = true;
        }

        $accept = array_merge($defaults, $accept);

        // normalize the MIME, extension and type from strings into arrays
        if (is_string($accept['mime']) === true) {
            $accept['mime'] = array_map(
                fn ($mime) => $mime['value'],
                Str::accepted($accept['mime'])
            );
        }

        if (is_string($accept['extension']) === true) {
            $accept['extension'] = array_map(
                'trim',
                explode(',', $accept['extension'])
            );
        }

        if (is_string($accept['type']) === true) {
            $accept['type'] = array_map(
                'trim',
                explode(',', $accept['type'])
            );
        }

        return $accept;
    }
}
