<?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 App\Services\Calculators\Department\DepartmentCalculator;
use Illuminate\Support\Collection;

class ReportCalculator
{

    //uses CalculatorByStandardClass
    private $standardCalculators = [
        'questionsByStandard' => Calculators\SubjectByStandardQuestions::class,
        'answersByStandard' => Calculators\SubjectByStandardAnswers::class,
    ];
    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,

        'semesterQuestions' => Calculators\SemesterQuestions::class,
        'semesterAnswers' => Calculators\SemesterAnswers::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,
        'semesterAnswers' => DepartmentCalculators\SemesterAnswers::class,
        
        'subjectByStandardAnswers' => DepartmentCalculators\SubjectByStandardAnswers::class,
        'subjectAverageByStandard' => DepartmentCalculators\SubjectAverageByStandard::class,
    ];

    private $departmentCalculatorsByStandard = [
        'subjectByStandardAnswers' => DepartmentCalculators\SubjectByStandardAnswers::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 = [];

        $standards = [4, 5, 7];

        foreach($standards as $standard){
                $questions = $answerSet->getSubjectByStandardQuestions($standard);
                $data['standardsQuestions'][(string)$standard] = collect($questions);
                foreach($questions as $question){
                    $value = $answerSet->getSubjectByStandardAnswers($standard)->where('question_id', '=', $question->id)->pluck('mark')->avg();
                    $value = round($value, 2);
                    $avgAnswers[$question->id] = $value;
                }
                $data['standardsAverageMark'][(string)$standard] = collect($avgAnswers);
        }

        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) {
                if($key == "subjectByStandardAnswers" || $key == "subjectAverageByStandard"){
                    foreach($standards as $standard){
                        foreach ($this->departmentCalculatorsByStandard as $key => $calculatorClass) {
                            $departmentData[$key][$standard] = $this->getDepartmentCalculatorByStandard($calculatorClass, $department)->calculate($answerSet, $standard);
                        }
                    }
                }
                else
                    $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
     * @return \App\Services\Calculators\CalculatorByStandard
     */
    protected function getCalculatorByStandard(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;
    }



    /**
     * @param string $calculatorClass
     * @param \App\Models\Department $department
     * @return \App\Services\Calculators\Department\DepartmentCalculatorByStandard;
     */
    protected function getDepartmentCalculatorByStandard(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;
    }
}