<?php


namespace App\Model\Facades\Writers;


use App\Model\Crates\WorldOfWarcraft\Race;
use App\Model\Entities\GameData\Mmorpg\Faction;
use App\Model\Entities\GameData\Mmorpg\InstanceType;
use App\Model\Entities\GameData\Mmorpg\Server;
use App\Model\Facades\Readers\FactionReader;
use App\Model\Facades\Readers\GameReader;
use App\Model\Facades\Readers\InstanceReader;
use App\Model\Facades\Readers\RegionReader;
use App\Model\Facades\Readers\WowApiReader;
use App\Model\Services\RaceService;
use Throwable;

class WowApiWriter extends BaseWriter
{
    /** @var  GameReader */
    protected $gameReader;
    /** @var  WowApiReader */
    protected $wowApiReader;
    /** @var  RegionReader */
    protected $regionReader;
    /** @var  RegionWriter */
    protected $regionWriter;
    /** @var  ServerWriter */
    protected $serverWriter;
    /** @var  CharacterClassWriter */
    protected $characterClassWriter;
    /** @var  RaceWriter */
    protected $raceWriter;
    /** @var  RaceService */
    protected $raceService;
    /** @var  FactionReader */
    protected $factionReader;
    /** @var  FactionWriter */
    protected $factionWriter;
    /** @var  InstanceReader */
    protected $instanceReader;
    /** @var  InstanceWriter */
    protected $instanceWriter;

    public function inject(GameReader $gameReader, WowApiReader $wowApiReader,
                           RegionReader $regionReader, RegionWriter $regionWriter,
                           ServerWriter $serverWriter, CharacterClassWriter $characterClassWriter,
                           RaceWriter $raceWriter, RaceService $raceService,
                           FactionReader $factionReader, FactionWriter $factionWriter,
                           InstanceReader $instanceReader, InstanceWriter $instanceWriter)
    {
        $this->gameReader = $gameReader;
        $this->wowApiReader = $wowApiReader;
        $this->regionReader = $regionReader;
        $this->regionWriter = $regionWriter;
        $this->serverWriter = $serverWriter;
        $this->characterClassWriter = $characterClassWriter;
        $this->raceWriter = $raceWriter;
        $this->raceService = $raceService;
        $this->factionReader = $factionReader;
        $this->factionWriter = $factionWriter;
        $this->instanceReader = $instanceReader;
        $this->instanceWriter = $instanceWriter;
    }

    public function importRegions($game)
    {
        // blizzard doesn't have api for regions list
        $regions = ['us' => 'United States',
                    'eu' => 'Europe',
                    'kr' => 'Korea',
                    'tw' => 'Taiwan'];
        $regionsInDb = $this->regionReader->getRegionsIdentifiers($game);

        try {
            $this->beginTransaction();
            foreach ($regions as $identifier => $name) {
                if (!in_array($identifier, $regionsInDb)) {                   //import records, that are not in DB yet
                    $this->regionWriter->createRegion($game, $name, $identifier);
                }
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    public function importRealms($game)
    {
        $regions = $this->regionReader->getRegions($game);
        try {
            $this->beginTransaction();
            foreach ($regions as $region) {
                $realms = $this->wowApiReader->retrieveRealms($region);
                foreach ($realms as $realm) {
                    $server = $this->serverWriter->createServer($region, $realm->getName());
                    $server->setState(Server::ACTIVE);
                }
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    public function importFactions($game)
    {
        $factions = ['Horde', 'Alliance'];  // blizzard doesn't have api for factions
        $factionsInDb = $this->factionReader->getSelectBoxPairs($game);

        try {
            $this->beginTransaction();
            foreach ($factions as $factionName) {
                if (!in_array($factionName, $factionsInDb)) {                   //import records, that are not in DB yet
                    $this->factionWriter->createFaction($game, $factionName);
                }
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    public function importCharacterClasses($game)
    {
        $classes = $this->wowApiReader->retrieveCharacterClasses();
        try {
            $this->beginTransaction();
            foreach ($classes as $characterClass) {
                $this->characterClassWriter->createClass($game, $characterClass->getName());
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }

    }

    public function importRaces($game)
    {
        $gameEntity = $this->gameReader->validateGame($game);

        $races = $this->wowApiReader->retrieveRaces();
        $racesInDB = $this->raceService->getRacesNameAssoc($gameEntity);
        $factions = $this->factionReader->getFactions($gameEntity);
        try {
            $this->beginTransaction();
            foreach ($races as $race) {
                if (!isset($racesInDB[$race->getName()])) {
                    $raceEntity = $this->raceWriter->createRace($gameEntity, $race->getName());
                    $racesInDB[$race->getName()] = $raceEntity;
                } else {
                    $raceEntity = $racesInDB[$race->getName()];
                }
                foreach ($factions as $faction) {
                    if ($this->isAvailable($faction, $race)) {
                        $this->raceWriter->makeRaceAvailableToFaction($raceEntity, $faction);
                    } else {
                        $this->raceWriter->makeRaceUnavailableToFaction($raceEntity, $faction);
                    }
                }
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    public function importInstances($game)
    {
        $modesNames = [
            'DUNGEON_NORMAL' => 'normal',
            'DUNGEON_HEROIC' => 'heroic',
            'DUNGEON_MYTHIC' => 'mythic',
            'RAID_NORMAL' => 'normal',
            'RAID_10_NORMAL' => 'normal',
            'RAID_25_NORMAL' => 'normal',
            'RAID_10_HEROIC' => 'heroic',
            'RAID_25_HEROIC' => 'heroic',
            'RAID_LFR' => 'raid finder',
            'RAID_FLEX_LFR' => 'raid finder flexible',
            'RAID_FLEX_NORMAL' => 'normal flexible',
            'RAID_FLEX_HEROIC' => 'heroic flexible',
            'RAID_MYTHIC' => 'mythic'];

        $instances = $this->wowApiReader->retrieveInstances();
        $instancesInDB = $this->instanceReader->getInstancesNameAssoc($game);
        $instancesTypes = $this->instanceReader->getInstanceTypesNameAssoc($game);
        try {
            $this->beginTransaction();
            foreach ($instances as $instance) {
                if (!isset($instancesInDB[$instance->getName()])) {
                    if ($instance->isRaid()) {
                        $type = 'Raid';
                    } else {
                        $type = 'Dungeon';
                    }
                    $instanceEntity = $this->instanceWriter->createInstance($game, $instancesTypes[$type], $instance->getName());
                    foreach ($instance->getModes() as $mode) {
                        switch ($mode) {
                            case 'RAID_10_NORMAL':
                            case 'RAID_10_HEROIC':
                                $min = 10;
                                $max = 10;
                                break;
                            case 'RAID_25_NORMAL':
                            case 'RAID_25_HEROIC':
                            case 'RAID_MYTHIC':
                                $min = 25;
                                $max = 25;
                                break;
                            default:
                                $min = $instance->getMinimumPlayers();
                                $max = $instance->getMaximumPlayers();
                                break;
                        }
                        if ($mode === 'DUNGEON_HEROIC' || $mode === 'DUNGEON_MYTHIC') {
                            $level = $instance->getRequiredLevelHeroic();
                        } else {
                            $level = $instance->getRequiredLevelNormal();
                        }
                        $this->instanceWriter->createInstanceMode($instanceEntity, $modesNames[$mode], $level, $min, $max, $mode);
                    }
                    foreach ($instance->getBosses() as $boss) {
                        $this->instanceWriter->createBoss($instanceEntity, $boss->getName());
                    }
                }
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    public function importInstanceTypes($game)
    {
        $types = ['Dungeon' => InstanceType::PVE, 'Raid' => InstanceType::PVE, 'Battlegroud' => InstanceType::PVP, 'Arena' => InstanceType::PVP];
        $typesInDb = $this->instanceReader->getInstanceTypeNamePairs($game);
        try {
            $this->beginTransaction();
            foreach ($types as $name => $type) {
                if (!in_array($name, $typesInDb)) {
                    $this->instanceWriter->createInstanceType($game, $name, $type);
                }
            }
            $this->commit();
        } catch (Throwable $e) {
            $this->rollback();
            throw $e;
        }
    }

    protected function isAvailable(Faction $faction, Race $race)
    {
        if ($race->getSide() == 'neutral') {    // neutral is available to both factions
            return true;
        }

        return (strtolower($faction->getName()) == $race->getSide());
    }
}