<?php
/**
 * Holds the file controller.
 * @package Sakura
 */

namespace Sakura\Controllers;

use Phroute\Phroute\Exception\HttpMethodNotAllowedException;
use Phroute\Phroute\Exception\HttpRouteNotFoundException;
use Sakura\Config;
use Sakura\CurrentSession;
use Sakura\DB;
use Sakura\Exceptions\FileException;
use Sakura\File;
use Sakura\Template;
use Sakura\User;

/**
 * File controller, handles user uploads like avatars.
 * @package Sakura
 * @author Julian van de Groep <me@flash.moe>
 */
class FileController extends Controller
{
    /**
     * Possible modes.
     */
    private const MODES = [
        'avatar',
        'background',
        'header',
    ];

    /**
     * The base for serving a file.
     * @param string $data
     * @param string $mime
     * @param string $name
     * @return string
     */
    private function serve(string $data, string $mime, string $name): string
    {
        header("Content-Disposition: inline; filename={$name}");
        header("Content-Type: {$mime}");
        return $data;
    }

    /**
     * Handles file uploads.
     * @param string $mode
     * @param array $file
     * @param User $user
     * @throws FileException
     */
    private function upload(string $mode, array $file, User $user): void
    {
        switch ($file['error']) {
            case UPLOAD_ERR_OK:
                break;

            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                throw new FileException("Your file was too large!");

            case UPLOAD_ERR_PARTIAL:
                throw new FileException("The upload failed!");

            case UPLOAD_ERR_NO_TMP_DIR:
            case UPLOAD_ERR_CANT_WRITE:
                throw new FileException("Wasn't able to save the file, contact a staff member!");

            case UPLOAD_ERR_EXTENSION:
            default:
                throw new FileException("Something prevented the file upload!");
        }

        $tmpName = $file['tmp_name'];
        $meta = getimagesize($tmpName);

        if (!$meta || !in_array($meta[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)) {
            throw new FileException("Please upload a valid image!");
        }

        $maxWidth = config("file.{$mode}.max_width");
        $maxHeight = config("file.{$mode}.max_height");

        if ($meta[0] > $maxWidth
            || $meta[1] > $maxHeight) {
            throw new FileException("Your image can't be bigger than {$maxWidth}x{$maxHeight}" .
                ", yours was {$meta[0]}x{$meta[1]}!");
        }

        // Check file size
        $maxFileSize = config("file.{$mode}.max_file_size");

        if (filesize($tmpName) > $maxFileSize) {
            $maxSizeFmt = byte_symbol($maxFileSize);

            throw new FileException("Your image is not allowed to be larger than {$maxSizeFmt}!");
        }

        $ext = image_type_to_extension($meta[2]);
        $filename = "{$mode}_{$user->id}{$ext}";
        $file = File::create(file_get_contents($tmpName), $filename, $user);
        $this->delete($mode, $user);
        $column = "user_{$mode}";

        DB::table('users')
            ->where('user_id', $user->id)
            ->update([
                $column => $file->id,
            ]);
    }

    /**
     * Deletes a file.
     * @param string $mode
     * @param User $user
     */
    public function delete(string $mode, User $user): void
    {
        $fileId = $user->{$mode};

        if ($fileId) {
            (new File($fileId))->delete();
        }

        DB::table('users')
            ->where('user_id', $user->id)
            ->update(["user_{$mode}" => 0]);
    }

    /**
     * Catchall serve.
     * @param string $method
     * @param array $params
     * @return string
     */
    public function __call(string $method, array $params): ?string
    {
        if (!in_array($method, self::MODES)) {
            throw new HttpRouteNotFoundException;
        }

        $user = User::construct($params[0] ?? 0);

        if (session_check()) {
            $perm_var = "change" . ucfirst(strtolower($method));

            if (($user->id !== CurrentSession::$user->id || !$user->activated
                || $user->restricted || !$user->perms->{$perm_var})
                && !CurrentSession::$user->perms->manageProfileImages
            ) {
                throw new HttpMethodNotAllowedException;
            }

            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
                $error = null;

                try {
                    $this->upload($method, $_FILES['file'] ?? null, $user);
                } catch (FileException $e) {
                    $error = $e->getMessage();
                }

                return $this->json(compact('error'));
            } elseif ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
                $this->delete($method, $user);
                return null;
            }
        }

        $no_file = path('public/' . str_replace('%tplname%', Template::$name, config("user.{$method}_none")));
        $none = [
            'name' => basename($no_file),
            'data' => file_get_contents($no_file),
            'mime' => getimagesize($no_file)['mime'],
        ];

        if (!$user->activated || $user->restricted || !$user->{$method}) {
            return $this->serve($none['data'], $none['mime'], $none['name']);
        }

        $serve = new File($user->{$method});

        if (!$serve->id) {
            return $this->serve($none['data'], $none['mime'], $none['name']);
        }

        return $this->serve($serve->data, $serve->mime, $serve->name);
    }
}