<?php

namespace App\Repositories;

use App\Models\Email;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Database\Eloquent\Builder;
use App\Support\Query;
use Illuminate\Support\Facades\DB;
use App\Jobs\SendBulkEmail;
use App\Models\File;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class EmailRepository extends Repository
{
    public function __construct($event = null)
    {
        $this->for($event);
    }


    public function model(): ?Email
    {
        return new Email;
    }

    public function query($as = null)
    {
        $model = $this->model();
        return $model->newQuery();
    }

    public function sendEmail($request, $event)
    {

        $data = $request->get('data');
        $data = json_decode($data, true);

        $validator = Validator::make($data, [
            'recipients' => ['required'],
            'subject' => ['string', 'max:255'],
            'bcc' => ['nullable'],
            'body' => ['string', 'nullable'],
            'attachment' => ['array', 'nullable'],
            'status' => ['string'],
        ]);

        if ($validator->fails()) {
            validationErrorResponse($validator->errors());
        } else {

            $id = $data['emailId'] ?? null;
            $email = Email::find($id);

            if (!$email) {
                $emailData = $validator->validated();
                $email = new Email([
                    'event_id' => $event->id,
                ]);
                $email = $email->fill($emailData);
                $email->save();
            } else {
                $emailData = $validator->validated();
                $email = $email->fill($emailData);
            }

            //handling uploaded files
            $attachmentData = $this->handleEmailFiles($request, $email, $data);
            $email->attachments = $attachmentData;

            $email->save();

            if ($email->status == "processing") {
                // recipients
                $recipients = array_column($emailData['recipients'], 'email');
                // bcc
                $bccs = array_column($emailData['bcc'], 'email');
                $updatedBcc = [];

                foreach ($bccs as $bcc) {
                    $user = User::where('email', $bcc)->first();

                    if ($user) {
                        if ($user->do_not_send_emails) {
                            $ccEmails = array_filter(array_map('trim', explode(",", $user->cc_emails)));
                            if ($ccEmails) {
                                $updatedBcc = array_merge($updatedBcc, $ccEmails);
                            }
                        } else {
                            $updatedBcc[] = $bcc;
                        }
                    } else {
                        $updatedBcc[] = $bcc;
                    }
                }

                // valid bcc
                $validBcc = array_filter(array_map('trim', $updatedBcc), function($email) {
                    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
                });

                // send email to user
                dispatch(new SendBulkEmail(array_map('trim', $recipients), $validBcc, $event, $email));
            }

            return response([
                'status' => true,
                'message' => "Mail Created Successfully",
                'email' => $email
            ]);
        }
    }

    public function handleEmailFiles(Request $request, Email $email, $data)
    {

        $attachmentData = $data['attachments'];
        //handling uploaded files
        if ($request->hasFile('attachments')) {
            $files = $request->file('attachments');
            $fileContents = [];
            foreach ($files as $file) {
                $fileObj = $email->saveFile(
                    $file,
                    null,
                    "{$email->id}/files/",
                    "email.file",
                    null,
                    true
                );
                $fileContents[] = $fileObj->getFileObject();
            }

            $attachmentData = array_values(array_filter(array_merge($data['attachments'], $fileContents)));
        }
        //handling removed files
        $removedFiles = $data['removed_files'];

        if (isset($removedFiles)) {
            $fileIds = $removedFiles;
            foreach ($fileIds as $fileId) {

                $this->deleteFile($fileId);
            }
        }

        return $attachmentData;
    }


    public function delete(Email $email)
    {

        if (isset($email->attachments)) {
            foreach ($email->attachments as $attachment) {

                $this->deleteFile($attachment['file_id']);
            }
        }

        //delete directory
        Storage::deleteDirectory('private/media/emails/' . $email->id);
        $email->delete();
    }


    /**
     * @param $fileId
     * @return bool
     */
    public function deleteFile($fileId): bool
    {
        $file = File::find($fileId);
        if (!$file)
            return false;

        $delete = Storage::delete($file->filepath . '/' . $file->save_name);
        $file->delete();

        return true;
    }


    public function selectColumns(): array
    {
        return [
            'emails.id',
            'emails.recipients',
            'emails.bcc',
            'emails.subject',
            'emails.body',
            'emails.status',
            'emails.attachments',
            'emails.send_date',
            'emails.created_at',
        ];
    }

    public function scope($arguments, $callback = null)
    {
        //get query builder
        $query = $this->query()
            ->select($this->selectColumns());

        //applying different scopes based on the arguments
        $this->applyScope($query, $arguments);

        //resolve if there is any callback functions available
        return $this->resolve($query, $arguments, $callback);
    }


    public function applyScope(Builder $query, $arguments): Builder
    {
        $this->applyEventScope($query, $arguments);

        $this->applyStatus($query, $arguments);

        $this->applyOrder($query, $arguments);

        $this->applySearch($query, $arguments);

        return $query;
    }

    public function applyEventScope(Builder $query, $arguments): Builder
    {
        if ($arguments->event) {
            $query->where('emails.event_id', '=', $arguments->event->id);
        }

        return $query;
    }

    public function applyStatus(Builder $query, $arguments): Builder
    {
        if ($arguments->status) {

            $status = strtolower($arguments->status);

            $query->where('status', $status);
        }

        return $query;
    }


    public function applyOrder(Builder $query, Query $arguments): Builder
    {
        $emailColumns = [
            'subject' => 'emails.subject',
            'send_date' => 'emails.send_date',
            'recipients' => 'emails.recipients',
            'created_at' => 'emails.created_at',
        ];

        $sorts = json_decode($arguments->sort, true) ?? [];
        foreach ($sorts as $sort => $method) {
            if (array_key_exists($sort, $emailColumns)) {
                $query->orderBy($sort, $method);
            }
        }

        return $query;
    }


    public function applySearch(Builder $query, $arguments): Builder
    {
        if ($arguments->search) {
            $emailColumns = [
                'subject' => 'emails.subject',
                'send_date' => 'emails.send_date',
                'recipients' => 'emails.recipients',
                'created_at' => 'emails.created_at',
            ];
            $search = strtolower($arguments->search);
            $query->where(function ($query) use ($search, $emailColumns) {
                foreach ($emailColumns as $emailColumn) {
                    $query->orWhere(DB::raw("lower($emailColumn)"), 'like', "%{$search}%");
                }
            });
        }

        return $query;
    }

    public function listing($request, $paginate)
    {
        //building arguments
        $arguments = $this->arguments($request);

        //building query
        $query = $this->scope($arguments);

        return ($paginate && $arguments->paging != 'All') ? $query->paginate($arguments->paging, ['*'], 'page', $arguments->page) : $query->get();
    }
}
