<?php

namespace App\Services;

use App\Models\Department;
use Carbon\Carbon;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class ReportManager
{
    /** @var \Illuminate\Filesystem\FilesystemAdapter  */
    private $storage;

    /** @var string */
    private $directory = 'reports';

    private $relevantTables = [
        'answers', 'answer_sheets', 'codes'
    ];

    public function __construct(FilesystemAdapter $storage)
    {
        $this->storage = $storage;
    }

    /**
     * @return array
     */
    public function all()
    {
        return array_map(function ($path) {
            return basename($path);
        }, $this->storage->files($this->directory));
    }

    /**
     * @param string $filename
     * @return bool
     */
    public function exists(string $filename)
    {
        return $this->storage->exists($this->relativePath($filename));
    }

    /**
     * @param string $filename
     * @return \Symfony\Component\HttpFoundation\StreamedResponse
     */
    public function download(string $filename)
    {
        if (!$this->exists($filename)) {
            abort(404);
        }

        return $this->storage->download($this->relativePath($filename));
    }

    public function generate(string $filename = null)
    {
        if ($filename === null) {
            $filename = $this->guessArchiveName();
        }

        $departments = $this->prepareDepartments();
        $data = ReportCalculator::forDepartments($departments)->calculate();

        $raw = $this->renderReportView($data);
        $htmlName = $this->storeTemporaryHtml($raw);

        \CloudConvert::file($this->fullPath($htmlName))->to($docxpath = $this->fullPath($filename));

        $this->storage->delete($this->relativePath($htmlName));

        return $filename;
    }

    public function generateAndDownload(string $name = null)
    {
        if ($name === null) {
            $name = $this->guessTemporaryName();
        }

        $name = $this->generate($name);

        return response()->download(
            $this->fullPath($name),
            $name
        )->deleteFileAfterSend();
    }

    public function archive(string $filename = null)
    {
        $path = $this->generate($filename);

        $this->clear();

        return $this->download($path);
    }

    public function clear()
    {
        DB::transaction(function () {
            DB::statement("SET foreign_key_checks=0;");

            foreach ($this->relevantTables as $table) {
                $this->clearTable($table);
            }

            DB::statement("SET foreign_key_checks=1;");
        });
    }

    /**
     * @param string $filename
     * @return string
     */
    protected function relativePath(string $filename)
    {
        return $this->directory . DIRECTORY_SEPARATOR . $filename;
    }

    /**
     * @param string $filename
     * @return string
     */
    protected function fullPath(string $filename)
    {
        return $this->storage->path($this->relativePath($filename));
    }

    protected function guessArchiveName()
    {
        $name = "Izveštaj arhiva " . Carbon::now('Europe/Belgrade')->format("d-m-Y H:i:s");
        $extension = 'docx';

        return "{$name}.{$extension}";
    }

    protected function guessTemporaryName()
    {
        $name = "Izveštaj " . Carbon::now('Europe/Belgrade')->format('d-m-Y H:i:s');
        $extension = 'docx';

        return "{$name}.{$extension}";
    }

    protected function prepareDepartments()
    {
        return Department::with('subjects.teacher', 'subjects.assistant')->get();
    }

    protected function renderReportView(array $data)
    {
        return view('admin.export.report')->with($data)->render();
    }

    protected function storeTemporaryHtml(string $report)
    {
        $this->storage->put($this->relativePath($filename = Str::random() . ".html"), $report);

        return $filename;
    }

    private function clearTable(string $table)
    {
        DB::table($table)->truncate();
    }
}
