<?php
/**
 * Copyright 2020 Adobe
 * All Rights Reserved.
 *
 * NOTICE: Adobe permits you to use, modify, and distribute this file in
 * accordance with the terms of the Adobe license agreement accompanying
 * it.
 */
declare(strict_types=1);

namespace Sut\Domain\Compatibility\Data;

use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Sut\Domain\Issue\DTO\Issue;
use Sut\Domain\MRay\DTO\Module;

class ModuleResult
{
    /**
     * @var Module
     */
    private $module;

    /**
     * @var FileResult[]
     */
    private $files;

    /**
     * @var Issue[]
     */
    private $issues;

    /**
     * @var int
     */
    private $minIssueLevel;

    /**
     * @var Issue[]
     */
    private $filteredIssues;

    /**
     * @var IssuesSummaryResult
     */
    private $summary;

    /**
     * @var int
     */
    private $totalFiles;

    /**
     * @param Module $module
     * @param FileResult[] $files
     * @param int $minIssueLevel
     */
    public function __construct(Module $module, array $files, int $minIssueLevel)
    {
        $this->module = $module;
        $this->files = $files;
        $this->minIssueLevel = $minIssueLevel;
    }

    /**
     * @return Issue[]
     */
    public function getIssues(): array
    {
        if (!$this->issues) {
            $issues = [];
            foreach ($this->files as $file) {
                $issues[] = $file->getIssues();
            }
            $this->issues = array_merge([], ...$issues);
        }
        return $this->issues;
    }

    /**
     * @param int $minIssueLevel
     * @return array|Issue[]
     */
    public function getFilteredIssues(int $minIssueLevel = 0): array
    {
        $newMinIssueLevel = $minIssueLevel ?? $this->minIssueLevel;
        if (!isset($this->filteredIssues[$newMinIssueLevel])) {
            $this->filteredIssues[$newMinIssueLevel] = [];
            foreach ($this->getIssues() as $issue) {
                if ($issue->getLevel() <= $newMinIssueLevel) {
                    $this->filteredIssues[$newMinIssueLevel][] = $issue;
                }
            }
        }

        return $this->filteredIssues[$newMinIssueLevel];
    }

    /**
     * @return FileResult[]
     */
    public function getFiles(): array
    {
        usort($this->files, function (FileResult $result1, FileResult $result2) {
            return $result1->getPath() <=> $result2->getPath();
        });
        return $this->files;
    }

    /**
     * @return Module
     */
    public function getModule(): Module
    {
        return $this->module;
    }

    /**
     * @return IssuesSummaryResult
     */
    public function getSummary(): IssuesSummaryResult
    {
        if (!$this->summary) {
            $this->summary = new IssuesSummaryResult(
                $this->getIssues(),
                $this->minIssueLevel
            );
        }
        return $this->summary;
    }

    /**
     * @return int
     */
    public function getTotalNumberOfFiles(): int
    {
        if (!$this->totalFiles) {
            $this->totalFiles = iterator_count(
                new RecursiveIteratorIterator(
                    new RecursiveDirectoryIterator($this->module->getPath(), FilesystemIterator::SKIP_DOTS)
                )
            );
        }
        return $this->totalFiles;
    }

    /**
     * @return string[]
     */
    public function getIncompatibleFileLevels(): array
    {
        $files = [];
        foreach ($this->getIssues() as $issue) {
            $level = $issue->getLevel();
            if (isset($files[$issue->getFileName()])) {
                $level = min($files[$issue->getFileName()], $level);
            }
            $files[$issue->getFileName()] = $level;
        }
        return $files;
    }

    /**
     * @return string
     */
    public function getFilesSummary(): string
    {
        if ($this->getTotalNumberOfFiles() === 0) {
            return '00.00%';
        }
        $numberOfIncompatibleFiles = count($this->getIncompatibleFileLevels());
        $percent = ($numberOfIncompatibleFiles / $this->getTotalNumberOfFiles()) * 100;
        return  sprintf('%0.2f%% (%s/%s)', $percent, $numberOfIncompatibleFiles, $this->getTotalNumberOfFiles());
    }
}
