<?php

declare(strict_types=1);

namespace App\Service\Organization;

use App\Entity\Network\NetworkOrganization;
use App\Entity\Organization\Organization;
use App\Enum\Organization\OrganizationIdsEnum;
use App\Enum\Organization\SettingsProductEnum;
use App\Repository\Network\NetworkOrganizationRepository;
use App\Service\Network\Utils as NetworkUtils;
use App\Service\Utils\DatesUtils;
use App\Service\Utils\UrlBuilder;

/**
 * Service rassemblant des fonctions d'aide pour les questions se rapportant à l'organisation.
 */
class Utils
{
    public const START_DATE_KEY = 'dateStart';
    public const END_DATE_KEY = 'dateEnd';
    public const OUT_OF_NET_SUBDOMAIN = 'outofnet';

    public function __construct(
        private NetworkOrganizationRepository $networkOrganizationRepository,
        private NetworkUtils $networkUtils,
    ) {
    }

    /**
     * Teste si l'organisation est considérée comme une structure == n'a pas un produit manager.
     *
     * @see UtilsTest::testIsStructureTest()
     */
    public function isStructure(Organization $organization): bool
    {
        return $organization->getSettings()->getProduct() !== SettingsProductEnum::MANAGER
               && $organization->getSettings()->getProduct() !== SettingsProductEnum::MANAGER_PREMIUM;
    }

    /**
     * Teste si l'organisation est considérée comme un manager == a un produit manager standard.
     *
     * @see UtilsTest::testIsManagerTest()
     */
    public function isManager(Organization $organization): bool
    {
        return $organization->getSettings()->getProduct() === SettingsProductEnum::MANAGER;
    }

    /**
     * Teste si l'organisation est considérée comme un school == a un produit school standard ou premium.
     *
     * @see UtilsTest::testIsSchool()
     */
    public function isSchool(Organization $organization): bool
    {
        return $organization->getSettings()->getProduct() === SettingsProductEnum::SCHOOL
            || $organization->getSettings()->getProduct() === SettingsProductEnum::SCHOOL_PREMIUM;
    }

    /**
     * Teste si l'organisation est considérée comme un school == a un produit artiste standard ou premium.
     *
     * @see UtilsTest::testIsArtist()
     */
    public function isArtist(Organization $organization): bool
    {
        return $organization->getSettings()->getProduct() === SettingsProductEnum::ARTIST
            || $organization->getSettings()->getProduct() === SettingsProductEnum::ARTIST_PREMIUM;
    }

    /**
     * Teste si l'organisation est la structure 2iOpenservice.
     *
     * @see UtilsTest::testIsOrganizationIs2ios()
     */
    public function is2iosOrganization(Organization $organization): bool
    {
        return $this->isOrganizationIdEqualTo($organization, OrganizationIdsEnum::_2IOS);
    }

    /**
     * Test si l'organisation est la structure CMF.
     *
     * @see UtilsTest::testIsCMF()
     */
    public function isCMF(Organization $organization): bool
    {
        return $this->isOrganizationIdEqualTo($organization, OrganizationIdsEnum::CMF);
    }

    /**
     * Test si l'organisation est un dernier parent ET appartient à la CMF.
     *
     * @see UtilsTest::testIsLastParentAndCMF()
     */
    public function isLastParentAndCMF(Organization $organization): bool
    {
        return $this->networkOrganizationRepository->isLastParent($organization) && $this->networkUtils->isCMF($organization);
    }

    /**
     * Test si l'organisation est une structure (non manager) ET appartient à la CMF.
     *
     * @see UtilsTest::testIsStructureAndCMF()
     */
    public function isStructureAndCMF(Organization $organization): bool
    {
        return $this->isStructure($organization) && $this->networkUtils->isCMF($organization);
    }

    /**
     * Test si la structure est un manager ET qu'elle appartient à la CMF.
     *
     * @see UtilsTest::testIsManagerAndCMF()
     */
    public function isManagerAndCMF(Organization $organization): bool
    {
        return $this->isManager($organization) && $this->networkUtils->isCMF($organization);
    }

    /**
     * Test si l'organisation est un manager ET un dernier parent ET appartient à la CMF.
     *
     * @see UtilsTest::testIsManagerAndLastParentAndCMF()
     */
    public function isManagerAndLastParentAndCMF(Organization $organization): bool
    {
        return $this->isManager($organization) && $this->isLastParentAndCMF($organization);
    }

    /**
     * Test si l'organisation est un manager ET n'est pas un dernier parent ET appartient à la CMF.
     *
     * @see UtilsTest::testIsManagerAndNotLastParentAndCMF()
     */
    public function isManagerAndNotLastParentAndCMF(Organization $organization): bool
    {
        return $this->isManager($organization) && !$this->networkOrganizationRepository->isLastParent($organization) && $this->networkUtils->isCMF($organization);
    }

    /**
     * Test si l'id de l'organisation est celui passé en paramètre (doit faire partie des OrganizationIdsEnum).
     */
    public function isOrganizationIdEqualTo(Organization $organization, OrganizationIdsEnum $organizationIds): bool
    {
        return $organization->getId() === $organizationIds->value;
    }

    /**
     * Retourne l'année d'activité dans laquelle on se situe aujourd'hui.
     *
     * @throws \Exception
     *
     * @see UtilsTest::testGetOrganizationCurrentActivityYear()
     */
    public function getOrganizationCurrentActivityYear(Organization $organization): int
    {
        $today = DatesUtils::new('now');
        $year = (int) $today->format('Y');

        $musicalDate = $organization->getParameters()->getMusicalDate();
        if (empty($musicalDate)) {
            $musicalDate = new \DateTime($year.'-08-31');
        }

        $base_date = new \DateTime($musicalDate->format($year.'-m-d'));
        $dateStart = new \DateTime($year.'-01-01');

        if ($today >= $dateStart && $today < $base_date) {
            return $year - 1;
        } else {
            return $year;
        }
    }

    /**
     * Fonction permettant de récupérer les dates de début et de fin d'activité d'une structure selon une année.
     *
     * @return string[]
     *
     * @throws \Exception
     *
     * @see UtilsTest::testGetActivityPeriodsSwitchYear()
     */
    public function getActivityPeriodsSwitchYear(Organization $organization, ?int $year = null): array
    {
        // Utiliser l'année actuelle si $year est null
        if ($year === null) {
            $year = (int) DatesUtils::new()->format('Y');
        }

        $musicalDate = $organization->getParameters()->getMusicalDate();

        if (empty($musicalDate)) {
            $dateStart = DatesUtils::new($year.'-09-01');
            $dateEnd = DatesUtils::new(($year + 1).'-08-31');
        } else {
            $dateStart = DatesUtils::new($year.'-'.$musicalDate->format('m').'-'.$musicalDate->format('d'));
            $dateEnd = clone $dateStart;
            $dateEnd->add(new \DateInterval('P1Y'))->sub(new \DateInterval('P1D'));
        }

        return [
            self::START_DATE_KEY => $dateStart->format('Y-m-d'),
            self::END_DATE_KEY => $dateEnd->format('Y-m-d'),
        ];
    }

    /**
     * Permet de retrouver quelle sera la date d'activité correspondant à une date précise de l'année.
     *
     * @throws \Exception
     *
     * @see UtilsTest::testgetActivityYearSwitchDate()
     */
    public function getActivityYearSwitchDate(Organization $organization, \DateTimeInterface $date): int
    {
        $year = $date->format('Y');
        $musicalDate = $organization->getParameters()->getMusicalDate();

        if (empty($musicalDate)) {
            $startDate = new \DateTime($year.'-08-31');
        } else {
            $startDate = new \DateTime($musicalDate->format($year.'-m-d'));
        }

        $pivotDate = new \DateTime($year.'-01-01');

        if ($date >= $pivotDate && $date < $startDate) {
            return (int) ($year - 1);
        } else {
            return (int) $year;
        }
    }

    /**
     * Return the active subdomain of an organization as a string, or null.
     */
    public function getOrganizationActiveSubdomain(Organization $organization): mixed
    {
        foreach ($organization->getSubdomains() as $subdomain) {
            if ($subdomain->isActive()) {
                return $subdomain->getSubdomain();
            }
        }

        return null;
    }

    /**
     * Get the URL of the current website of the organization.
     *
     * @see https://ressources.opentalent.fr/display/SPEC/Preferences#Preferences-Siteinternet
     */
    public function getOrganizationWebsite(Organization $organization): mixed
    {
        $parameters = $organization->getParameters();

        if ($parameters->getDesactivateOpentalentSiteWeb()) {
            if ($parameters->getWebsite()) {
                return UrlBuilder::prependHttps($parameters->getWebsite());
            }

            return null;
        }

        if (!empty($parameters->getCustomDomain())) {
            return UrlBuilder::prependHttps($parameters->getCustomDomain());
        }

        $subdomain = $this->getOrganizationActiveSubdomain($organization);
        if (!$subdomain) {
            return null;
        }
        if ($subdomain === self::OUT_OF_NET_SUBDOMAIN) {
            return 'https://opentalent.fr';
        }

        return 'https://'.$subdomain.'.opentalent.fr';
    }

    /**
     * Does the organization own the given module.
     */
    public function hasModule(Organization $organization, string $module): bool
    {
        $modules = $organization->getSettings()->getModules();

        return !empty($modules) && in_array($module, $modules);
    }

    /**
     * Retourne le premier NetworkOrganization actif de la structure.
     *
     * @throws \Exception
     */
    public function getActiveNetworkOrganization(Organization $organization): ?NetworkOrganization
    {
        foreach ($organization->getNetworkOrganizations() as $networkOrganization) {
            if ($this->networkUtils->isNetworkOrganizationActiveNow($networkOrganization)) {
                return $networkOrganization;
            }
        }

        return null;
    }
}
