<?php

/*
 * This file is part of the package thucke/th-rating.
 *
 * For the full copyright and license information, please read the
 * LICENSE file that was distributed with this source code.
 */

namespace WapplerSystems\BookmarksLikesRatings\Service;

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;

/**
 * The voter
 */
class RichSnippetService extends AbstractExtensionService
{
    /**
     * Instances of AggregateRating may appear as properties of the following types
     * This list derives from Google's information about supported types in aggregateRating (20 May 2021)
     * (see https://developers.google.com/search/docs/data-types/review-snippet#aggregated-rating-type-definition)
     * @const array
     */
    protected const VALID_AGGREGATE_RATING_SCHEMA_TYPES = [
        'Book',
        'Audiobook',
        'Course',
        'CreativeWorkSeason',
        'PodcastSeason',
        'RadioSeason',
        'TVSeason',
        'CreativeWorkSeries',
        'BookSeries',
        'MovieSeries',
        'Periodical',
        'ComicSeries',
        'Newspaper',
        'PodcastSeries',
        'RadioSeries',
        'TVSeries',
        'VideoGameSeries',
        'Episode',
        'PodcastEpisode',
        'RadioEpisode',
        'TVEpisode',
        'Event',
        'BusinessEvent',
        'ChildrensEvent',
        'ComedyEvent',
        'CourseInstance',
        'DanceEvent',
        'DeliveryEvent',
        'EducationEvent',
        'EventSeries',
        'ExhibitionEvent',
        'Festival',
        'FoodEvent',
        'Hackathon',
        'LiteraryEvent',
        'MusicEvent',
        'PublicationEvent',
        'BroadcastEvent',
        'OnDemandEvent',
        'SaleEvent',
        'ScreeningEvent',
        'SocialEvent',
        'SportsEvent',
        'TheaterEvent',
        'VisualArtsEvent',
        'Game',
        'VideoGame',
        'HowTo',
        'Recipe',
        'AnimalShelter',
        'ArchiveOrganization',
        'AutomotiveBusiness',
        'AutoBodyShop',
        'AutoDealer',
        'AutoRental',
        'AutoRepair',
        'AutoWash',
        'GasStation',
        'MotorcycleDealer',
        'MotorcycleRepair',
        'ChildCare',
        'DryCleaningOrLaundry',
        'EmergencyService',
        'FireStation',
        'Hospital',
        'PoliceStation',
        'EmploymentAgency',
        'EntertainmentBusiness',
        'AdultEntertainment',
        'AmusementPark',
        'ArtGallery',
        'Casino',
        'ComedyClub',
        'MovieTheater',
        'NightClub',
        'FinancialService',
        'AccountingService',
        'AutomatedTeller',
        'BankOrCreditUnion',
        'InsuranceAgency',
        'FoodEstablishment',
        'Bakery',
        'BarOrPub',
        'Brewery',
        'CafeOrCoffeeShop',
        'Distillery',
        'FastFoodRestaurant',
        'IceCreamShop',
        'Restaurant',
        'Winery',
        'GovernmentOffice',
        'PostOffice',
        'HealthAndBeautyBusiness',
        'BeautySalon',
        'DaySpa',
        'HairSalon',
        'NailSalon',
        'TattooParlor',
        'HomeAndConstructionBusiness',
        'Electrician',
        'GeneralContractor',
        'HVACBusiness',
        'HousePainter',
        'Locksmith',
        'MovingCompany',
        'Plumber',
        'RoofingContractor',
        'InternetCafe',
        'LegalService',
        'Attorney',
        'Notary',
        'Library',
        'LodgingBusiness',
        'BedAndBreakfast',
        'Campground',
        'Hostel',
        'Hotel',
        'Motel',
        'Resort',
        'MedicalBusiness',
        'CovidTestingFacility',
        'Optician',
        'Pharmacy',
        'Physician',
        'ProfessionalService',
        'RadioStation',
        'RealEstateAgent',
        'RecyclingCenter',
        'SelfStorage',
        'ShoppingCenter',
        'SportsActivityLocation',
        'BowlingAlley',
        'ExerciseGym',
        'GolfCourse',
        'HealthClub',
        'PublicSwimmingPool',
        'SkiResort',
        'SportsClub',
        'StadiumOrArena',
        'TennisComplex',
        'Store',
        'AutoPartsStore',
        'BikeStore',
        'BookStore',
        'ClothingStore',
        'ComputerStore',
        'ConvenienceStore',
        'DepartmentStore',
        'ElectronicsStore',
        'Florist',
        'FurnitureStore',
        'GardenStore',
        'GroceryStore',
        'HardwareStore',
        'HobbyShop',
        'HomeGoodsStore',
        'JewelryStore',
        'LiquorStore',
        'MensClothingStore',
        'MobilePhoneStore',
        'MovieRentalStore',
        'MusicStore',
        'OfficeEquipmentStore',
        'OutletStore',
        'PawnShop',
        'PetStore',
        'ShoeStore',
        'SportingGoodsStore',
        'TireShop',
        'ToyStore',
        'WholesaleStore',
        'TelevisionStation',
        'TouristInformationCenter',
        'TravelAgency',
        'MediaObject',
        '3DModel',
        'AudioObject',
        'DataDownload',
        'ImageObject',
        'Barcode',
        'LegislationObject',
        'MusicVideoObject',
        'VideoObject',
        'Movie',
        'MusicPlaylist',
        'MusicAlbum',
        'MusicRelease',
        'MusicRecording',
        'Organization',
        'Airline',
        'Consortium',
        'Corporation',
        'EducationalOrganization',
        'CollegeOrUniversity',
        'ElementarySchool',
        'HighSchool',
        'MiddleSchool',
        'Preschool',
        'School',
        'FundingScheme',
        'GovernmentOrganization',
        'LibrarySystem',
        'LocalBusiness',
        'MedicalOrganization',
        'NGO',
        'NewsMediaOrganization',
        'Dentist',
        'DiagnosticLab',
        'MedicalClinic',
        'VeterinaryCare',
        'PerformingGroup',
        'DanceGroup',
        'MusicGroup',
        'TheaterGroup',
        'Project',
        'FundingAgency',
        'ResearchProject',
        'SportsOrganization',
        'SportsTeam',
        'WorkersUnion',
        'Product',
        'IndividualProduct',
        'ProductCollection',
        'ProductGroup',
        'ProductModel',
        'SomeProducts',
        'Vehicle',
        'BusOrCoach',
        'Car',
        'Motorcycle',
        'MotorizedBicycle',
        'SoftwareApplication',
        'MobileApplication',
        'WebApplication'
    ];

    /**
     * @var string
     */
    protected $schema = 'Product';

    /**
     * @var string
     */
    protected $anchor;

    /**
     * @var string
     */
    protected $name;

    /**
     * @var string
     */
    protected $description;

    /**
     * @var array
     */
    protected $richSnippetConfig;

    /**
     * @param array $settings
     * @return bool
     */
    public function setRichSnippetConfig(array $settings): bool
    {
        $this->logger->log(LogLevel::DEBUG, 'setRichSnippetConfig Entry point', $settings);
        $this->richSnippetConfig['tablename'] = $settings['ratetable'];
        $this->richSnippetConfig['richSnippetFields'] = $settings['richSnippetFields'];

        if (is_array($this->richSnippetConfig['richSnippetFields'])) {
            $this->logger->log(
                LogLevel::DEBUG,
                'setRichSnippetConfig Exit point',
                $this->richSnippetConfig['richSnippetFields']
            );

            return true;
        }

        $this->logger->log(LogLevel::DEBUG, 'setRichSnippetConfig Exit point');

        return false;
    }

    /**
     * @return string|false
     */
    public function getRichSnippetConfig()
    {
        return GeneralUtility::makeInstance(\WapplerSystems\BookmarksLikesRatings\Service\JsonService::class)
            ->encodeToJson($this->richSnippetConfig);
    }

    /**
     * @return string
     */
    public function getSchema(): string
    {
        return $this->schema;
    }

    /**
     * @param string|null $schema
     * @throws \WapplerSystems\BookmarksLikesRatings\Exception\InvalidAggregateRatingSchemaTypeException if parameter is invalid
     */
    public function setSchema(?string $schema): void
    {
        if (!empty($schema)) {
            if (in_array($schema, self::VALID_AGGREGATE_RATING_SCHEMA_TYPES, true)) {
                $this->schema = $schema;
            } else {
                throw new \WapplerSystems\BookmarksLikesRatings\Exception\InvalidAggregateRatingSchemaTypeException(
                    LocalizationUtility::translate(
                        'error.richSnippetConfiguration.AggregateRatingPropertySchema',
                        'ThRating'
                    ),
                    1521487362
                );
            }
        }
    }

    /**
     * @return string
     */
    public function getAnchor(): string
    {
        return $this->anchor;
    }

    /**
     * @param string $anchor
     */
    public function setAnchor(string $anchor): void
    {
        $this->anchor = $anchor;
    }

    /**
     * @param string|null $name
     */
    public function setName(?string $name): void
    {
        $this->name = $name;
    }

    /**
     * @return string|null
     */
    public function getName(): ?string
    {
        return $this->name;
    }

    /**
     * @return string|null
     */
    public function getDescription(): ?string
    {
        return $this->description;
    }

    /**
     * @param string|null $description
     */
    public function setDescription(?string $description): void
    {
        $this->description = $description;
    }

    /**
     * @param int $uid
     * @return RichSnippetService
     *@throws \WapplerSystems\BookmarksLikesRatings\Exception\InvalidAggregateRatingSchemaTypeException
     */
    public function getRichSnippetObject(int $uid): self
    {
        $this->logger->log(LogLevel::DEBUG, 'getRichSnippetObject Entry point');
        $this->setSchema($this->richSnippetConfig['richSnippetFields']['aggregateRatingSchemaType']);
        if (empty($this->richSnippetConfig['richSnippetFields']['name'])) {
            $this->logger->log(LogLevel::INFO, 'No name field defined - skipping database access');
            unset($this->name, $this->description);
            throw new \TYPO3\CMS\Core\Exception();
        } else {
            /** @var QueryBuilder $queryBuilder */
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
                ->getQueryBuilderForTable($this->richSnippetConfig['tablename']);

            //fetch whole row from database
            /** @var array $row */
            $row = $queryBuilder
                ->select('*')
                ->from($this->richSnippetConfig['tablename'])
                ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid)))
                ->execute()
                ->fetch();

            $this->logger->log(LogLevel::DEBUG, 'Data fetched', $row);
            $this->setName($row[$this->richSnippetConfig['richSnippetFields']['name']]);

            $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
            $this->setDescription($row[$this->richSnippetConfig['richSnippetFields']['description']]);
            //$this->setDescription($contentObjectRenderer->cObjGetSingle('TEXT', $this->richSnippetConfig['richSnippetFields']['description']));
            //$this->setDescription($contentObjectRenderer->render($this->richSnippetConfig['richSnippetFields']['description']));
        }
        $this->logger->log(LogLevel::DEBUG, 'getRichSnippetObject Exit point', (array)$this);

        return $this;
    }
}