<?php
namespace Misuzu\Http\Handlers;

use Misuzu\GitInfo;
use Misuzu\Users\User;
use Misuzu\Users\UserNotFoundException;
use Misuzu\Users\Assets\StaticUserImageAsset;
use Misuzu\Users\Assets\UserImageAssetInterface;
use Misuzu\Users\Assets\UserAssetScalableInterface;

final class AssetsHandler extends Handler {
    private const TYPES = [
        'js' => [
            'root' => MSZ_ROOT . '/assets/js',
            'mime' => 'application/javascript; charset=utf-8',
        ],
        'css' => [
            'root' => MSZ_ROOT . '/assets/css',
            'mime' => 'text/css; charset=utf-8',
        ],
    ];

    public function __construct() {
        $GLOBALS['misuzuBypassLockdown'] = true;
        parent::__construct();
    }

    private static function recurse(string $dir): string {
        $str = '';
        $dir = rtrim(realpath($dir), '/') . '/*';
        $dirs = [];

        foreach(glob($dir) as $path) {
            if(is_dir($path)) {
                $dirs[] = $path;
                continue;
            }

            if(MSZ_DEBUG)
                $str .= "/*** {$path} ***/\n";
            $str .= trim(file_get_contents($path));
            $str .= "\n\n";
        }

        foreach($dirs as $path)
            $str .= self::recurse($path);

        return $str;
    }

    public function serveComponent($response, $request, string $fileName) {
        $name = pathinfo($fileName, PATHINFO_FILENAME);
        $type = pathinfo($fileName, PATHINFO_EXTENSION);

        $entityTag = sprintf('%s.%s/%s', $name, $type, GitInfo::hash());

        if(!MSZ_DEBUG && $name === 'debug')
            return 404;
        if(!MSZ_DEBUG && $request->getHeaderFirstLine('If-None-Match') === '"' . $entityTag . '"')
            return 304;

        if(array_key_exists($type, self::TYPES)) {
            $type = self::TYPES[$type];
            $path = ($type['root'] ?? '') . '/' . $name;

            if(is_dir($path)) {
                $response->setContentType($type['mime'] ?? 'application/octet-stream');
                $response->setCacheControl(MSZ_DEBUG ? 'no-cache' : 'must-revalidate');
                $response->setEntityTag($entityTag);
                return self::recurse($path);
            }
        }
    }

    private function canViewAsset($request, User $assetUser): bool {
        return !$assetUser->isBanned() || (
            User::hasCurrent()
            && parse_url($request->getHeaderFirstLine('Referer'), PHP_URL_PATH) === url('user-profile')
            && perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_USERS)
        );
    }

    private function serveUserAsset($response, $request, UserImageAssetInterface $assetInfo): void {
        $contentType = $assetInfo->getMimeType();
        $publicPath = $assetInfo->getPublicPath();
        $fileName = $assetInfo->getFileName();

        if($assetInfo instanceof UserAssetScalableInterface) {
            $dimensions = (int)($request->getParam('res', FILTER_SANITIZE_NUMBER_INT) ?? $request->getParam('r', FILTER_SANITIZE_NUMBER_INT));

            if($dimensions > 0) {
                $assetInfo->ensureScaledExists($dimensions);
                $contentType = $assetInfo->getScaledMimeType($dimensions);
                $publicPath = $assetInfo->getPublicScaledPath($dimensions);
                $fileName = $assetInfo->getScaledFileName($dimensions);
            }
        }

        $response->accelRedirect($publicPath);
        $response->setContentType($contentType);
        $response->setFileName($fileName, false);
    }

    public function serveAvatar($response, $request, string $fileName) {
        $userId = (int)pathinfo($fileName, PATHINFO_FILENAME);
        $type = pathinfo($fileName, PATHINFO_EXTENSION);

        if($type !== '' && $type !== 'png')
            return 404;

        $assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/no-avatar.png', MSZ_PUBLIC);

        try {
            $userInfo = User::byId($userId);

            if(!$this->canViewAsset($request, $userInfo)) {
                $assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/banned-avatar.png', MSZ_PUBLIC);
            } elseif($userInfo->hasAvatar()) {
                $assetInfo = $userInfo->getAvatarInfo();
            }
        } catch(UserNotFoundException $ex) {}

        $this->serveUserAsset($response, $request, $assetInfo);
    }

    public function serveProfileBackground($response, $request, string $fileName) {
        $userId = (int)pathinfo($fileName, PATHINFO_FILENAME);
        $type = pathinfo($fileName, PATHINFO_EXTENSION);

        if($type !== '' && $type !== 'png')
            return 404;

        try {
            $userInfo = User::byId($userId);
        } catch(UserNotFoundException $ex) {}

        if(empty($userInfo) || !$userInfo->hasBackground() || !$this->canViewAsset($request, $userInfo)) {
            $response->setContent('');
            return 404;
        }

        $this->serveUserAsset($response, $request, $userInfo->getBackgroundInfo());
    }

    public function serveLegacy($response, $request) {
        $assetUserId = (int)$request->getParam('u', FILTER_SANITIZE_NUMBER_INT);

        switch($request->getParam('m')) {
            case 'avatar':
                $this->serveAvatar($response, $request, $assetUserId);
                return;
            case 'background':
                $this->serveProfileBackground($response, $request, $assetUserId);
                return;
        }

        $response->setContent('');
        return 404;
    }
}
