<?php

namespace Inside\DESK\Console;

use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Inside\Authentication\Models\User;
use Inside\Content\Models\Contents\Users;
use Inside\DESK\Jobs\SendStatisticsExportNotification;
use Inside\Permission\Facades\Role;
use Inside\Statistics\Facades\Stats;
use League\Csv\CannotInsertRecord;
use League\Csv\Exception;
use League\Csv\Writer;
use Symfony\Component\Console\Style\SymfonyStyle;

class ExportUsersInformations extends Command
{
    /**
     * @var string
     */
    protected $name = 'desk:user:infos:export';

    /**
     * @var string
     */
    protected $signature = 'desk:user:infos:export {startDate : Date from which we get the users infos (format Y-m-d H:i:s)}
    {endDate : end Date to get the users infos (format Y-m-d H:i:s)}
    {--only-first-export}
    {--only-second-export}
    {--notify}
    {--limit=}
    {--email=}
    ';

    /**
     * @var string
     */
    protected $description = 'Export users informations between two dates';

    /**
     * the content types to retrieve for the export
     * @var array
     */
    protected $types = [
        'news',
        'memorandums',
    ];

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

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

    /**
     * @param array $headers
     * @return Writer
     * @throws CannotInsertRecord
     * @throws Exception
     */
    public function createCsvWithHeaders(array $headers)
    {
        if (!Storage::disk('protected')->exists('export')) {
            Storage::disk('protected')->makeDirectory('exports', 0755, true); //creates directory
        }
        $csv = Writer::createFromString();
        $csv->setDelimiter(';');
        $csv->setOutputBOM(Writer::BOM_UTF8);
        $csv->insertOne($headers);
        $csv->setEscape('');
        return $csv;
    }

    /**
     * @param string $uuid
     * @return int|void
     */
    protected function countAllVisibleContent(string $uuid, string $langcode)
    {
        $fields = ['brands', 'countries', 'management_modes', 'profiles'];
        $counter = 0;
        foreach ($this->types as $type) {
            $query = DB::table(type_to_table($type))
                ->where('langcode', $langcode)
                ->where('status', 1)
                ->whereBetween('published_at', [$this->startDate, $this->endDate]);

            if (!DB::table('inside_users_roles')->where('user_uuid', $uuid)->where('role_id', 1)->exists()) {
                foreach ($fields as $field) {
                    $references = DB::table('inside_pivots')->where([
                        'parent_type' => type_to_class('users'),
                        'parent_uuid' => $uuid,
                        'related_langcode' => $langcode,
                        'related_type' => type_to_class($field),
                    ])->pluck('related_uuid')->toArray();

                    $total = DB::table(type_to_table($field))->where('langcode', $langcode)->count();
                    if (count($references) === $total) {
                        continue;
                    }

                    // we get the references in french and english because user can see both
                    $hosts = DB::table(type_to_table($field))->whereIn('uuid', $references)->pluck('uuid_host')->toArray();
                    $contents = DB::table(type_to_table($field))->whereIn('uuid_host', $hosts)->pluck('uuid')->toArray();
                    $query->join('inside_pivots AS inside_pivots_'.$field, function ($join) use ($type, $field, $contents) {
                        $join->on('inside_pivots_'.$field.'.parent_uuid', '=', 'inside_content_'.$type.'.uuid')
                            ->where(function ($subQuery) use ($field, $contents) {
                                $subQuery->whereIn('inside_pivots_'.$field.'.related_uuid', $contents);
                            });
                    });
                }
            }
            $counter += $query->groupBy(type_to_table($type).'.uuid')->get()->count();
        }
        return $counter;
    }

    public function sendNotifications(string $title): void
    {
        if (!$this->option('notify')) {
            return;
        }

        $roles = env('STATISTICS_EXPORT_ROLE', 'super_administrator');
        $roles = explode(',', $roles);
        $url = Storage::disk('protected')->url('exports/'.$title.'.csv');

        foreach ($roles as $roleName) {
            $role = \Inside\Permission\Models\Role::query()->where('name', $roleName)->first();

            if (!$role instanceof \Inside\Permission\Models\Role) {
                continue;
            }

            $users = Role::listRoleUsers($role->id, null, null, true);

            foreach ($users['data'] as $user) {
                Queue::push(new SendStatisticsExportNotification($user, $title, $url));
            }
        }
    }

    public function handleFirstExport(int $limit, ?string $email): void
    {
        $headers = [
            'UserEmail',
            'UserName',
            'ResortCode',
            'NbrArticlesViewByUser',
            'NbrPotentialArticlesByUser',
            'Month',
            'Year',
        ];
        $title = 'User_Statistics_'.Carbon::parse($this->startDate)->format('YmdHis');

        $csv = $this->createCsvWithHeaders($headers);
        $filters = [
            'only_viewed' => true,
            'from' => Carbon::parse($this->startDate)->format('Y-m-d'),
            'to' => Carbon::parse($this->endDate)->format('Y-m-d'),
        ];
        $this->info('creating the first csv...');
        $users = User::query()->where('status', 1);
        if ($email) {
            $users = $users->where('email', $email)->orWhere('name', $email)->get();
        }
        if ($limit) {
            $users = $users->take($limit)->get();
        }
        $month = Carbon::parse($this->startDate)->englishMonth;
        $year = Carbon::parse($this->startDate)->year;

        /** @var SymfonyStyle $output */
        $output = $this->getOutput();
        $bar = $output->createProgressBar($users->count());
        $users->each(function ($user) use ($filters, &$csv, $bar, $month, $year) {
            $bar->setMessage('adding statistics for user '.$user->email);
            $line = [];
            $line[] = $user->email;
            $line[] = $user->name;
            $line[] = DB::table('inside_content_users')->select('fra_code')->where('uuid', $user->uuid)->first()->fra_code;
            $viewedContents = Stats::getUserViewedContentList($user, $this->types, $filters);
            $line[] = $viewedContents['viewed'];
            $line[] = $this->countAllVisibleContent($user->uuid, $user->langcode);
            $line[] = $month;
            $line[] = $year;
            $csv->insertOne($line);
            $bar->advance();
        });
        $users = null;
        Storage::disk('protected')->put('exports/'.$title.'.csv', $csv->getContent());
        $csv = null;
        $bar->finish();

        $this->sendNotifications($title);
    }

    public function handleSecondExport(): void
    {
        $headers = [
            'ArticleTitle',
            'UserEmail',
            'UserName',
            'ResortCode',
            'Month',
            'Year',
        ];
        $filters = [
            'from' => Carbon::parse($this->startDate)->format('Y-m-d'),
            'to' => Carbon::parse($this->endDate)->format('Y-m-d'),
            'not_read' => false,
        ];
        $this->info('creating the second csv...');
        $title = 'Content_Statistics_'.Carbon::parse($this->startDate)->format('YmdHis');
        $csv = $this->createCsvWithHeaders($headers);

        $month = Carbon::parse($this->startDate)->englishMonth;
        $year = Carbon::parse($this->startDate)->year;

        foreach ($this->types as $type) {
            $contents = call_user_func(type_to_class($type).'::query')
                ->where('status', 1)
                ->get();
            $this->info('handling the content type '.$type);
            foreach ($contents as $content) {
                $users = Stats::getContentReaders($content, $filters);
                if ($users->count() === 0) {
                    continue;
                }
                $this->info("\nhandling ".$users->count().' users who saw content '.$content->title);

                /** @var SymfonyStyle $output */
                $output = $this->getOutput();
                $bar = $output->createProgressBar($users->count());
                // some contents have \n or &nbsp char in the title ...
                $contentTitle = str_replace("\n", '', $content->title);
                $contentTitle = str_replace("\xc2\xa0", ' ', $contentTitle);
                foreach ($users as $user) {
                    $userContent = Users::query()->find($user['uuid']);

                    $bar->setMessage($user['email']);
                    $line = [];
                    $line[] = $contentTitle;
                    $line[] = $user['email'];
                    $line[] = $user['name'];
                    $line[] = $userContent->fra_code;
                    $line[] = $month;
                    $line[] = $year;
                    $csv->insertOne($line);
                    $bar->advance();
                }
            }
        }
        Storage::disk('protected')->put('exports/'.$title.'.csv', $csv->getContent());

        $this->sendNotifications($title);
    }

    public function handle(): void
    {
        /** @var string $startDate */
        $startDate = $this->argument('startDate');

        /** @var string $endDate */
        $endDate = $this->argument('endDate');

        $onlyFirst = (bool)$this->option('only-first-export');
        $onlySecond = (bool)$this->option('only-second-export');
        $limit = (int)$this->option('limit');

        /** @var string|null $email */
        $email = $this->option('email');

        try {
            $this->startDate = Carbon::parse($startDate)->format('Y-m-d H:i:s');
            $this->endDate = Carbon::parse($endDate)->format('Y-m-d H:i:s');
        } catch (\Exception $e) {
            dd("Please use a valid date with the format Y-m-d (for example : 2021-08-31) for both values");
        }

        if (!$onlySecond) {
            $this->handleFirstExport($limit, $email);
        }
        if (!$onlyFirst) {
            $this->handleSecondExport();
        }
    }
}
