<?php

namespace Spatie\Mailcoach\Domain\Audience\Actions\Subscribers;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Spatie\Mailcoach\Domain\Audience\Enums\SubscriberExportStatus;
use Spatie\Mailcoach\Domain\Audience\Jobs\CompleteSubscribersExportJob;
use Spatie\Mailcoach\Domain\Audience\Jobs\ExportSubscribersChunkJob;
use Spatie\Mailcoach\Domain\Audience\Mails\ExportSubscribersResultMail;
use Spatie\Mailcoach\Domain\Audience\Models\SubscriberExport;
use Spatie\Mailcoach\Domain\Shared\Traits\UsesMailcoachModels;
use Spatie\Mailcoach\Livewire\Audience\SegmentSubscribersComponent;
use Spatie\Mailcoach\Livewire\Audience\SubscribersComponent;
use Spatie\Mailcoach\Mailcoach;
use Spatie\SimpleExcel\SimpleExcelWriter;
use Throwable;

class ExportSubscribersAction
{
    use UsesMailcoachModels;

    protected ?Authenticatable $user;

    protected bool $sendNotification = true;

    protected SubscriberExport $subscriberExport;

    public function execute(
        SubscriberExport $subscriberExport,
        ?Authenticatable $user = null,
        bool $sendNotification = true
    ): void {
        $this
            ->initialize($subscriberExport, $user, $sendNotification)
            ->exportSubscribers()
            ->notify();
    }

    protected function initialize(SubscriberExport $subscriberExport, ?Authenticatable $user, bool $sendNotification = true): self
    {
        $this->subscriberExport = $subscriberExport;
        $this->user = $user;
        $this->sendNotification = $sendNotification;

        return $this;
    }

    protected function exportSubscribers(): self
    {
        Auth::setUser($this->user);

        try {
            $this->subscriberExport->update([
                'status' => SubscriberExportStatus::Exporting,
                'exported_subscribers_count' => 0,
            ]);

            $tempDisk = Storage::disk(config('mailcoach.tmp_disk'));
            $path = "export-file-{$this->subscriberExport->created_at->format('Y-m-d H:i:s')}.csv";

            $tempDisk->put($path, '');

            $writer = SimpleExcelWriter::create($tempDisk->path($path));

            $header = [
                'email',
                'first_name',
                'last_name',
                'tags',
                'subscribed_at',
                'unsubscribed_at',
                'extra_attributes',
            ];

            $writer->addHeader($header);
            $writer->close();

            $filters = $this->subscriberExport->filters;

            $subscriberIds = Arr::pull($filters, 'subscriber_ids', []);

            if (count($subscriberIds)) {
                $query = self::getSubscriberClass()::query()->whereIn('id', $subscriberIds);
            } else {
                $search = Arr::pull($filters, 'search');

                /** We set up the component to get the same query as the datatable displayed */
                if (isset($filters['segment_id']) && $segment = self::getTagSegmentClass()::find($filters['segment_id'])) {
                    $component = new SegmentSubscribersComponent;
                    $component->mount($this->subscriberExport->emailList, $segment);
                    unset($filters['segment_id']);
                } else {
                    $component = new SubscribersComponent;
                    $component->mount($this->subscriberExport->emailList);
                }

                $component->tableFilters = $filters;
                $component->tableSearch = $search ?? '';
                $component->bootedInteractsWithTable();
                $query = $component->getTable()->getQuery();
                $component->filterTableQuery($query);
            }

            $jobs = [];

            $query
                ->pluck('id')
                ->chunk(1_000)
                ->each(function ($subscriberIds) use ($header, $path, &$jobs) {
                    $jobs[] = new ExportSubscribersChunkJob($this->subscriberExport, $path, $subscriberIds->all(), $header);
                });

            $jobs[] = new CompleteSubscribersExportJob($this->subscriberExport, $path);

            Bus::chain($jobs)->dispatch();
        } catch (Throwable $exception) {
            report($exception);

            $this->subscriberExport->addError($exception->getMessage());
            $this->subscriberExport->update(['status' => SubscriberExportStatus::Failed]);
        }

        return $this;
    }

    protected function notify(): void
    {
        if (! $this->sendNotification) {
            return;
        }

        if (! $this->user) {
            return;
        }

        if (empty($this->user->email)) {
            return;
        }

        try {
            Mail::mailer(Mailcoach::defaultTransactionalMailer())
                ->to($this->user->email)->send(new ExportSubscribersResultMail($this->subscriberExport));
        } catch (Throwable $e) {
            report($e);

            return;
        }
    }
}
