<?php

declare(strict_types=1);

/**
 * Class FakePodcastsAnalyticsSeeder Inserts Fake Analytics in the database
 *
 * @copyright  2020 Ad Aures
 * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
 * @link       https://castopod.org/
 */

namespace App\Database\Seeds;

use App\Entities\Episode;
use App\Entities\Podcast;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use CodeIgniter\Database\Seeder;
use Exception;
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;

class FakePodcastsAnalyticsSeeder extends Seeder
{
    public function run(): void
    {
        $jsonUserAgents = json_decode(
            file_get_contents('https://raw.githubusercontent.com/opawg/user-agents/master/src/user-agents.json'),
            true,
            512,
            JSON_THROW_ON_ERROR,
        );

        $jsonRSSUserAgents = json_decode(
            file_get_contents(
                'https://raw.githubusercontent.com/opawg/podcast-rss-useragents/master/src/rss-ua.json',
            ),
            true,
            512,
            JSON_THROW_ON_ERROR,
        );

        $podcast = (new PodcastModel())->first();

        if (! $podcast instanceof Podcast) {
            throw new Exception("COULD NOT POPULATE DATABASE:\n\tCreate a podcast with episodes first.\n");
        }

        $firstEpisode = (new EpisodeModel())
            ->selectMin('published_at')
            ->first();

        if (! $firstEpisode instanceof Episode) {
            throw new Exception("COULD NOT POPULATE DATABASE:\n\tCreate an episode first.");
        }

        for (
            $date = strtotime((string) $firstEpisode->published_at);
            $date < strtotime('now');
            $date = strtotime(date('Y-m-d', $date) . ' +1 day')
        ) {
            $analyticsPodcasts = [];
            $analyticsPodcastsByHour = [];
            $analyticsPodcastsByCountry = [];
            $analyticsPodcastsByEpisode = [];
            $analyticsPodcastsByPlayer = [];
            $analyticsPodcastsByRegion = [];

            $episodes = (new EpisodeModel())
                ->where('podcast_id', $podcast->id)
                ->where('`published_at` <= UTC_TIMESTAMP()', null, false)
                ->findAll();
            foreach ($episodes as $episode) {
                $age = floor(($date - strtotime((string) $episode->published_at)) / 86400);
                $probability1 = floor(exp(3 - $age / 40)) + 1;

                for (
                    $lineNumber = 0;
                    $lineNumber < random_int(1, (int) $probability1);
                    ++$lineNumber
                ) {
                    $probability2 = floor(exp(6 - $age / 20)) + 10;

                    $player =
                        $jsonUserAgents[
                            random_int(1, count($jsonUserAgents) - 1)
                        ];
                    $service =
                        $jsonRSSUserAgents[
                            random_int(1, count($jsonRSSUserAgents) - 1)
                        ]['slug'];
                    $app = $player['app'] ?? '';
                    $device = $player['device'] ?? '';
                    $os = $player['os'] ?? '';
                    $isBot = $player['bot'] ?? 0;

                    $fakeIp =
                        random_int(0, 255) .
                        '.' .
                        random_int(0, 255) .
                        '.' .
                        random_int(0, 255) .
                        '.' .
                        random_int(0, 255);

                    $cityReader = new Reader(WRITEPATH . 'uploads/GeoLite2-City/GeoLite2-City.mmdb');

                    $countryCode = 'N/A';
                    $regionCode = 'N/A';
                    $latitude = null;
                    $longitude = null;
                    try {
                        $city = $cityReader->city($fakeIp);

                        $countryCode = $city->country->isoCode ?? 'N/A';

                        $regionCode = $city->subdivisions === []
                            ? 'N/A'
                            : $city->subdivisions[0]->isoCode;
                        $latitude = round((float) $city->location->latitude, 3);
                        $longitude = round((float) $city->location->longitude, 3);
                    } catch (AddressNotFoundException) {
                        //Bad luck, bad IP, nothing to do.
                    }

                    $hits = random_int(0, (int) $probability2);

                    $analyticsPodcasts[] = [
                        'podcast_id'       => $podcast->id,
                        'date'             => date('Y-m-d', $date),
                        'duration'         => random_int(60, 3600),
                        'bandwidth'        => random_int(1000000, 10000000),
                        'hits'             => $hits,
                        'unique_listeners' => $hits,
                    ];
                    $analyticsPodcastsByHour[] = [
                        'podcast_id' => $podcast->id,
                        'date'       => date('Y-m-d', $date),
                        'hour'       => random_int(0, 23),
                        'hits'       => $hits,
                    ];
                    $analyticsPodcastsByCountry[] = [
                        'podcast_id'   => $podcast->id,
                        'date'         => date('Y-m-d', $date),
                        'country_code' => $countryCode,
                        'hits'         => $hits,
                    ];
                    $analyticsPodcastsByEpisode[] = [
                        'podcast_id' => $podcast->id,
                        'date'       => date('Y-m-d', $date),
                        'episode_id' => $episode->id,
                        'age'        => $age,
                        'hits'       => $hits,
                    ];
                    $analyticsPodcastsByPlayer[] = [
                        'podcast_id' => $podcast->id,
                        'date'       => date('Y-m-d', $date),
                        'service'    => $service,
                        'app'        => $app,
                        'device'     => $device,
                        'os'         => $os,
                        'is_bot'     => $isBot,
                        'hits'       => $hits,
                    ];
                    $analyticsPodcastsByRegion[] = [
                        'podcast_id'   => $podcast->id,
                        'date'         => date('Y-m-d', $date),
                        'country_code' => $countryCode,
                        'region_code'  => $regionCode,
                        'latitude'     => $latitude,
                        'longitude'    => $longitude,
                        'hits'         => $hits,
                    ];
                }
            }

            $this->db
                ->table('analytics_podcasts')
                ->ignore(true)
                ->insertBatch($analyticsPodcasts);
            $this->db
                ->table('analytics_podcasts_by_hour')
                ->ignore(true)
                ->insertBatch($analyticsPodcastsByHour);
            $this->db
                ->table('analytics_podcasts_by_country')
                ->ignore(true)
                ->insertBatch($analyticsPodcastsByCountry);
            $this->db
                ->table('analytics_podcasts_by_episode')
                ->ignore(true)
                ->insertBatch($analyticsPodcastsByEpisode);
            $this->db
                ->table('analytics_podcasts_by_player')
                ->ignore(true)
                ->insertBatch($analyticsPodcastsByPlayer);
            $this->db
                ->table('analytics_podcasts_by_region')
                ->ignore(true)
                ->insertBatch($analyticsPodcastsByRegion);
        }
    }
}