<?php

namespace App\Services;

use App\Models\Department;
use App\Services\Calculators;
use App\Services\Calculators\AnswerSet;
use App\Services\Calculators\Department as DepartmentCalculators;
use Illuminate\Support\Collection;

class ReportCalculator
{
    private $overallCalculators = [
        'schoolYear' => Calculators\SchoolYear::class,

        'surveyedCount' => Calculators\SurveyedCount::class,
        'studentTotal' => Calculators\StudentTotal::class,
        'surveyedPerc' => Calculators\SurveyedPercentage::class,

        'departmentAverage' => Calculators\DepartmentAverage::class,
        'teacherAverage' => Calculators\TeacherAverage::class,

        'subjectCount' => Calculators\SubjectCount::class,
        'subjectOverview' => Calculators\SubjectOverview::class,

        'otherQuestions' => Calculators\OtherQuestions::class,
        'otherAnswers' => Calculators\OtherAnswers::class,
    ];

    private $departmentCalculators = [
        'name' => DepartmentCalculators\Name::class,
        'shorthand' => DepartmentCalculators\Shorthand::class,

        'surveyed' => DepartmentCalculators\Surveyed::class,
        'total' => DepartmentCalculators\TotalStudents::class,
        'percentage' => Calculators\Department\SurveyedPercentage::class,

        'markCount' => DepartmentCalculators\TotalMarks::class,
        'averageMark' => DepartmentCalculators\AverageMark::class,

        'teacherCount' => DepartmentCalculators\TeacherCount::class,
        'teacherAverage' => DepartmentCalculators\TeacherAverage::class,

        'subjectCount' => DepartmentCalculators\SubjectCount::class,
        'subjectAverage' => DepartmentCalculators\SubjectAverage::class,

        // year,
        // surveyed,
        // teacherCount,
        // teacherAverage(numeric, word),
        // subjectCount,
        // subjects(name, average, teacher, teacherAverage, assistant, assistantAverage),
        // markCount,
        // otherAnswers(keyed by question ID)
        'years' => DepartmentCalculators\YearsOverview::class,

        'otherAnswers' => DepartmentCalculators\OtherAnswers::class,
    ];

    private $departmentCalculatorInstances = [

    ];

    /** @var \Illuminate\Support\Collection<\App\Models\Department> */
    private $departments;

    public function __construct(Collection $departments)
    {
        $this->departments = $departments;
    }

    public static function forDepartments(Collection $departments)
    {
        return new static($departments);
    }

    public function calculate()
    {
        $answerSet = $this->getAnswerSet();
        $data = [];

        foreach ($this->overallCalculators as $key => $calculatorClass) {
            $calculator = $this->getCalculator($calculatorClass);
            $data[$key] = $calculator->calculate($answerSet);
            unset($calculator);
        }

        foreach ($this->departments as $department) {
            foreach ($this->departmentCalculators as $key => $calculatorClass) {
                $departmentData[$key] = $this->getDepartmentCalculator($calculatorClass, $department)->calculate($answerSet);
            }

            $data['departments'][] = collect($departmentData);
        }

        return $data;
    }

    /**
     * @return \App\Services\Calculators\AnswerSet
     */
    protected function getAnswerSet()
    {
        return tap(new AnswerSet())->withDepartments($this->departments);
    }

    /**
     * @param string $calculatorClass
     * @return \App\Services\Calculators\Calculator
     */
    protected function getCalculator(string $calculatorClass)
    {
        return new $calculatorClass;
    }

    /**
     * @param string $calculatorClass
     * @param \App\Models\Department $department
     * @return \App\Services\Calculators\Department\DepartmentCalculator;
     */
    protected function getDepartmentCalculator(string $calculatorClass, Department $department)
    {
        if (!array_key_exists($calculatorClass, $this->departmentCalculatorInstances)) {
            $calculator = new $calculatorClass($department);
            $this->departmentCalculatorInstances[$calculatorClass] = $calculator;
        } else {
            $calculator = tap($this->departmentCalculatorInstances[$calculatorClass])->setDepartment($department);
        }

        return $calculator;
    }
}