<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\WebrootFile;
use App\Utility\NullTranslator;
use App\Component\Sorter\FileSortType;
use App\Component\Sorter\FileSorter;

abstract class WebrootController extends AbstractController
{
    private ?string $project_dir = null;
    private ?TranslatorInterface $tranlsator = null;
    private ?EntityManagerInterface $entitymanager = null;
    protected ?NullTranslator $nulltranslator = null;
    
    
    public function __construct(KernelInterface $kernel,TranslatorInterface $translator,EntityManagerInterface $entitymanager)
    {
        $this->entity_manager = $entitymanager;
        $this->project_dir = $kernel->getProjectDir();
        $this->translator = $translator;
        $this->nulltranslator = new NullTranslator();
        $this->entitymanager = $entitymanager;
    }
    
    public function getHeaderTitleFiglet(): ?string
    {
        $filename = getenv("HEADER_TITLE_FIGLET");
        if (!$filename || !strlen($filename) || !file_exists($filename)) {
            $filename = join(DIRECTORY_SEPARATOR,[dirname(dirname(__DIR__)),"templates","webroot.txt"]);
        }
        $figlet_file = fopen($filename,"r");
        $header_figlet = fread($figlet_file, filesize($filename));
        fclose($figlet_file);
        $title = "<div class=\"center\"><pre>"
                . $header_figlet
                . "</pre></div>";
            return $title;
        return $title;
    }
    
    protected function getControllerVariables(): array
    {
        return [
            "header_title" => $this->getHeaderTitleFiglet(),
        ];      
    }
    
    public function trans(string $message,array $args=[],string $domain="messages",?string $locale=null): string
    {
        return $this->translator->trans($message,$args,domain:$domain,locale:$locale);
    }
    
    protected function getFilePermissions(string $url): array
    {
        return $this->entitymanager->getRepository(WebrootFile::class)
                ->findFilePermissionsByUrl($this->getUser(),$url);
    }
    
    protected function getFileResponse($abspath): Response
    {
        if (!is_file($abspath)) {
            throw new FileException();
        }
        
        $response = new BinaryFileResponse($abspath);
        $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE);
        return $response;
    }
    
    protected function getAbspathByUrl(string $url): ?string
    {
        return $this->entitymanager
                ->getRepository(WebrootFile::class)
                ->findAbspathByUrl($url);
    }
    
    public function getDisplayFileSize(int $size): string
    {
        $display_size = "";
        if ($size > 1073741824) {
            $size_type = "GB";
            
            $display_size .= ((int) ($size / 1073741824))
                    . "."
                    .  ((int) ((($size % 1073741824) * 10) / 1073741824))
                    . " GiB";
        } elseif ($size > 1048576) {
            $display_size .= ((int) ($size / 1048576))
                    . "."
                    . ((int) ((($size % 1048576) * 10) / 1048576))
                    . " MiB";
        } elseif ($size > 1024) {
            $display_size .= ((int) ($size / 1024))
                . "."
                . ((int) ((($size % 1024) * 10) / 1024))
                . " KiB";
        } else {
            $display_size .= $size . " B";
        }
        return $display_size;
    }
    
    public function getDisplayMtime(int $mtime): string
    {
        $dt = new \DateTime();
        $dt->setTimestamp($mtime);
        return $dt->format($this->trans("directory.timefmt",domain:'mydevel.webroot.app'));
    }
    
    protected function getDirectoryList(string $url): array
    {
        $abspath = $this->getAbspathByUrl($url);
        
        if (!$abspath || !is_dir($abspath)) {
            throw new FileException();
        }
        $file_repos = $this->entitymanager->getRepository(WebrootFile::class);
        $user = $this->getUser();
        $roles = $user->getRoles();
        if (in_array('ROLE_SUPERADMIN', $roles) || in_array('ROLE_ADMIN',$roles)) {
            $show_dotfiles = true;
        } else {
            $show_dotfiles = false;
        }
        
        $dirlist = [];
        
        foreach (scandir($abspath) as $dirent) {
            if ($dirent === '.' || $dirent === '..') {
                continue;
            }
            if ($dirent[0] === '.' && ! $show_dotfiles) {
                continue;
            }
            
            $fileurl = $url . '/' . urlencode($dirent);
            $file_abspath = $abspath . DIRECTORY_SEPARATOR . $dirent;
            $fileperm = $file_repos->findFilePermissionsByUrl($user,$fileurl);
            
            if (!$fileperm['read']) {
                continue;
            }
            $mtime = filemtime($file_abspath);
            if (is_file($file_abspath)) {
                $filesize = filesize($file_abspath);
                
                $dirlist[] = [
                    "name" => $dirent,
                    "icon" => "icon-file",
                    "href" => $fileurl,
                    "size" => $filesize,
                    "display_size" => $this->getDisplayFileSize($filesize),
                    "delete" => $fileperm['delete'],
                    "write" => $fileperm['write'],
                    "mtime" => $mtime,
                    "display_mtime" => $this->getDisplayMtime($mtime),
                    "read" => true,
                    "dir" => false,
                ];
            } elseif (is_dir($file_abspath)) {
                $dirlist[] = [
                    "name" => $dirent,
                    "icon"=>"icon-directory",
                    "href" => $fileurl,
                    "size" => "0",
                    "display_size" => "-",
                    "delete" => $fileperm['delete'],
                    "write" => $fileperm['write'],
                    "mtime" => $mtime,
                    "display_mtime" => $this->getDisplayMtime($mtime),
                    "read" => true,
                    "dir" => true,
                ];
            }
        }
        return $dirlist;
    }
    
    protected function getDirectoryResponse(string $url,array $routeconf, FileSortType $sort=FileSortType::TYPE_ASC) : Response
    {
        $dirlist = $this->getDirectoryList($url);
        $dirperm = $this->getFilePermissions($url);
        if (!$dirperm['read']) {
            throw AccessDeniedHttpException("You are not allowed to accesss this resource!");
        }
        
        $sort_url = [
            "type" => $url . "?S=T;0=A",
            "name" => $url . "?S=N;T=A",
            "size" => $url . "?S=S;T=A",
            "date" => $url . "?S=D;T=A",
        ];
        
        $sorter = FileSorter::class;
        switch ($sort) {
            case FileSortType::TYPE_ASC:
                uasort($dirlist,[$sorter,'sortTypeAsc']);
                $sort_url["type"] = $url . "?S=T;O=D";
                breaK;
            case FileSortType::TYPE_DESC:
                uasort($dirlist,[$sorter,'sortTypeDesc']);
                break;
            case FileSortType::NAME_ASC:
                uasort($dirlist,[$sorter,'sortNameAsc']);
                $sort_url["name"] = $url . "?S=N;O=D";
               break;
            case FileSortType::NAME_DESC:
                uasort($dirlist,[$sorter,'sortNameDesc']);
                break;
            case FileSortType::SIZE_ASC:
                uasort($dirlist,[$sorter,'sortSizeAsc']);
                $sort_url["size"] = $url ."?S=S;O=D";
                break;
            case FileSortType::SIZE_DESC:
                uasort($dirlist,[$sorter,'sortSizeDesc']);
                break;
            case FileSortType::DATE_ASC:
                uasort($dirlist,[$sorter,'sortDateAsc']);
                $sort_url["date"] = $url . "?S=D;O=D";
                break;
            case FileSortType::DATE_DESC:
                uasort($dirlist,[$sorter,'sortDateDesc']);
                break;
            default:
                break;
        }
        
        return $this->render('webroot/directory.html.twig', array_merge(
            $this->getControllerVariables(), [
                "msg"=> [
                    'filename' => $this->trans("directory.filename",domain:'mydevel.webroot.app'),
                    'mtime' => $this->trans("directory.mtime",domain:'mydevel.webroot.app'),
                    'filesize' => $this->trans("directory.filesize",domain:'mydevel.webroot.app'),
                    'title' => $this->trans("directory.title",["url"=>$url],domain:'mydevel.webroot.app'),
                ],
                "parent_url"=>$routeconf['parent_url'],
                "url"=>$url,
                "read"=>$dirperm['read'],
                "write"=>$dirperm['write'],
                "delete"=>$dirperm['delete'],
                "upload_url"=>($routeconf['path'] 
                    ? $this->generateUrl($routeconf['upload_route'],["path"=>$routeconf['path']]) . "/"
                    : substr($this->generateUrl($routeconf['upload_route'],['path'=>"."]),0,-1)),
                "mkdir_url"=>($routeconf['path'] 
                    ? $this->generateUrl($routeconf['mkdir_route'],["path"=>$routeconf['path']]) . "/"
                    : substr($this->generateUrl($routeconf['mkdir_route'],['path'=>"."]),0,-1)),
                "dir_entries"=>$dirlist,
                "sort_url" => $sort_url,
            ]
        ));
    }
    
    public function parseSort(?string $sort): FileSortType 
    {
        if (!$sort) {
            return FileSortType::TYPE_ASC;
        }
        $sa = explode(";",$sort);
        $s0 = $sa[0];
        if (sizeof($sa) > 1) {
            $s1 = explode("=",$sa[1]);
            if (sizeof($s1) !== 2) {
                $s1 = ['O','A'];
            }
        } else {
            $s1 = ['O','A'];
        }
        
        
        $ret = FileSortType::TYPE_ASC;
        switch ($s0) {
            case 'T':
                switch ($s1[1]) {
                    case 'D':
                        $ret = FileSortType::TYPE_DESC;
                        break;
                    case 'A':
                    default:
                        $ret = FileSortType::TYPE_ASC;
                        break;
                }
                break;
            case 'N':
                switch ($s1[1]) {
                    case 'D':
                        $ret = FileSortType::NAME_DESC;
                        break;
                    case 'A':
                    default:
                        $ret = FileSortType::NAME_ASC;
                        break;
                }
                break;
            case 'S':
                switch ($s1[1]) {
                    case 'D':
                        $ret = FileSortType::SIZE_DESC;
                        break;
                    case 'A':
                    default:
                        $ret = FileSortType::SIZE_ASC;
                        break;
                }
                break;
            case 'D':
                switch ($s1[1]) {
                    case 'D':
                        $ret = FileSortType::DATE_DESC;
                        break;
                    case 'A':
                    default:
                        $ret = FileSortType::DATE_ASC;
                        break;
                }
                break;
        }
        return $ret;
    }
    
    protected function getUrlResponse(string $url,array $routeconf,FileSortType $sort= FileSortType::TYPE_ASC): Response
    {
        if (!$this->getFilePermissions($url)['read']) {
            return $this->redirectToRoute("app_login",['_target_path'=> $url]);
        }
                
        $abspath = $this->getAbspathByUrl($url);
        
        if (!file_exists($abspath)) {
            throw new NotFoundHttpException();
        }
        
        
        if (is_file($abspath)) {
            return $this->getFileResponse($abspath);
        }
        if (is_dir($abspath)) {
            return $this->getDirectoryResponse($url,$routeconf,$sort);
        }

        throw FileException();
    }
}
