<?php

namespace App\Lib;

use App\Constants\Status;
use App\Models\Admin;
use App\Models\AdminNotification;
use App\Models\Department;
use App\Models\Priority;
use App\Models\SpamFilter;
use App\Models\SupportTicket;
use App\Models\SupportTicketAttachment;
use App\Models\SupportTicketReply;
use App\Models\TicketLog;
use App\Models\TicketStatus;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;

class TicketStore
{
    protected $files;
    protected $allowedExtension = ['jpg', 'png', 'jpeg', 'pdf', 'doc', 'docx'];
    protected $company;
    protected $department;
    protected $user;
    protected $request;
    protected $admin;
    protected $supportTicket;
    protected $createdVia;
    protected $assignStaff;
    protected $priority;
    protected $ticketStatus;

    public function __construct(Request $request, $createdVia = null)
    {
        $this->request    = $request;
        $this->createdVia = $createdVia;
        $this->admin      = ($createdVia == Status::CREATED_VIA_ADMIN) ? auth('admin')->user() : null;
        $this->company    = company();
    }

    public function getSupportTicket()
    {
        return $this->supportTicket->load([
            'tags',
            'replies' => function ($query) {
                $query->orderBy('created_at', 'desc');
            },
            'replies.admin',
            'replies.attachments',
            'department',
            'admin',
            'assignStaff',
            'user',
            'priority',
            'ticketStatus',
        ]);
    }

    public function getAllowedExtension()
    {
        return $this->allowedExtension;
    }

    public function validation($extraRule = [], $department = null)
    {
        $this->files = $this->request->file('attachments');

        $validationRule = [
            'attachments'     => [
                function ($attribute, $value, $fail) {
                    foreach ($this->files as $file) {
                        $ext = strtolower($file->getClientOriginalExtension());
                        if (!in_array($ext, $this->allowedExtension)) {
                            return $fail("Only " . implode(', ', $this->allowedExtension) . " files are allowed");
                        }
                    }
                    if (count($this->files) > 5) {
                        return $fail("Maximum 5 files can be uploaded");
                    }
                },
            ],
            'user_email'      => 'required|email',
            'subject'         => 'required|string',
            'description'     => 'required|string',
            'department'      => 'required|integer',
            'assign_staff_id' => 'nullable|integer',
            'status'          => 'required|integer',
            'priority'        => 'required|integer',
        ];

        if ($this->createdVia == Status::CREATED_VIA_ADMIN) {
            $validationRule['firstname'] = 'required|string';
            $validationRule['lastname']  = 'required|string';
        }

        if ($this->createdVia == Status::CREATED_VIA_EMAIL) {
            $validationRule['email_message_id'] = 'required|string';
        }

        if ($extraRule) {
            $validationRule = array_merge($validationRule, $extraRule);
        }

        $validator = Validator::make($this->request->all(), $validationRule);

        if ($validator->fails()) {
            throw new ValidationException($validator);
        }

        if (!$department) {
            $this->department = Department::active()->where('id', $this->request->department);

            if ($this->createdVia == Status::CREATED_VIA_ADMIN) {
                $this->department->whereHas('admins', function ($admin) {
                    $admin->where('admin_id', auth('admin')->id());
                });
            }

            $this->department = $this->department->firstOrFail();
        } else {
            $this->department = $department;
        }

        if ($this->request->priority) {
            $this->priority = Priority::active()->findOrFail($this->request->priority);
        }

        $this->ticketStatus = TicketStatus::active()->findOrFail($this->request->status);

        return true;
    }

    public function replyValidation($supportTicket)
    {
        $this->files = $this->request->file('attachments');

        $validationRule = [
            'attachments' => [
                'nullable',
                'array',
                function ($attribute, $value, $fail) {
                    foreach ($this->files ?? [] as $file) {
                        $ext = strtolower($file->getClientOriginalExtension());
                        if (!in_array($ext, $this->allowedExtension)) {
                            return $fail("Only png, jpg, jpeg, pdf, doc, docx files are allowed");
                        }
                    }

                    if (count($this->files ?? []) > 5) {
                        return $fail("Maximum 5 files can be uploaded");
                    }
                },
            ],
            'description' => 'required|string',
            'department'  => 'nullable|integer',
            'admin'       => 'nullable|integer',
            'status'      => 'nullable|integer',
            'priority'    => 'nullable|integer',
        ];

        $validator = Validator::make($this->request->all(), $validationRule);

        if ($validator->fails()) {
            throw new ValidationException($validator);
        }

        $this->supportTicket = $supportTicket;

        if ($this->request->department) {
            $this->department = Department::active()->findOrFail($this->request->department);
        } else {
            $this->department = $this->supportTicket->department;
        }

        if ($this->request->admin) {
            $this->assignStaff = Admin::findOrFail($this->request->admin);
        }

        if ($this->request->priority) {
            $this->priority = Priority::active()->findOrFail($this->request->priority);
        } else {
            $this->priority = $this->supportTicket->priority;
        }

        if ($this->request->status) {
            $this->ticketStatus = TicketStatus::active()->findOrFail($this->request->status);
        } else {
            $this->ticketStatus = $this->supportTicket->ticketStatus;
        }

        $this->user = $this->supportTicket->user;
        return true;
    }

    public function handleUser($user = null)
    {
        if (!$user) {
            $user = User::where('email', $this->request->user_email)->first();

            if (!$user) {
                $user            = new User();
                $user->firstname = $this->request->firstname;
                $user->lastname  = $this->request->lastname;
                $user->email     = $this->request->user_email;
                $user->mobile    = $this->request->mobile;
                $user->save();
            }
        }

        $this->user = $user;
    }

    public function storeSupportTicket($departmentData = [])
    {
        $supportTicket                   = new SupportTicket();
        $supportTicket->ticket_number    = generateTicketNumber($this->company->ticket_number_format);
        $supportTicket->admin_id         = $this->admin->id ?? 0;
        $supportTicket->user_id          = $this->user->id;
        $supportTicket->department_id    = $this->request->department;
        $supportTicket->assign_staff_id  = $this->request->assign_staff_id ?? 0;
        $supportTicket->priority_id      = $this->request->priority;
        $supportTicket->ticket_status_id = $this->request->status;
        $supportTicket->subject          = $this->request->subject;
        $supportTicket->created_via      = $this->createdVia;
        $supportTicket->email_message_id = $this->request->email_message_id ?? '';
        $supportTicket->department_data  = $departmentData;
        $supportTicket->save();

        $this->supportTicket = $supportTicket;

        $this->storeAdminNotification();

        if ($this->admin) {
            TicketLog::storeLog("New ticket added by {$this->admin->name}", $this->supportTicket->id, $this->admin->id);
        } else {
            TicketLog::storeLog("New ticket opened by customer", $this->supportTicket->id);
        }

        return $this->supportTicket;
    }

    public function storeReply($firstReply = false)
    {
        $supportTicketReply                    = new SupportTicketReply();
        $supportTicketReply->support_ticket_id = $this->supportTicket->id;
        $supportTicketReply->admin_id          = $this->admin->id ?? 0;
        $supportTicketReply->description       = purifyHtml($this->request->description);
        $supportTicketReply->reply_id          = $this->request->reply_id;
        $supportTicketReply->is_private        = $this->request->is_private ? 1 : 0;
        $supportTicketReply->ip_address        = getRealIP();
        $supportTicketReply->save();

        if ($this->request->hasFile('attachments')) {
            $uploadAttachment = $this->storeSupportAttachments($supportTicketReply->id);
            if ($uploadAttachment != 200) {
                return 'File could not upload';
            }
        }

        $this->spamFilter();

        if (!$firstReply && !$supportTicketReply->is_private) {
            if ($this->request->department) {
                $this->supportTicket->department_id = $this->request->department;
                TicketLog::storeLog("Department change to {$this->department->name} by {$this->admin->name}", $this->supportTicket->id, $this->admin->id);
            }

            if ($this->request->admin) {
                $this->supportTicket->assign_staff_id = $this->request->admin;
                TicketLog::storeLog("Assign staff to {$this->assignStaff->name} by {$this->admin->name}", $this->supportTicket->id, $this->admin->id, 'assigned', $this->assignStaff->id);
            }

            if ($this->supportTicket->assign_staff_id && $this->request->admin == 0) {
                $this->supportTicket->assign_staff_id = 0;
                TicketLog::storeLog("Unassigned ticket (by {$this->admin->name})", $this->supportTicket->id, $this->admin->id);
            }

            if ($this->request->priority) {
                $this->supportTicket->priority_id = $this->request->priority;
                TicketLog::storeLog("Priority change to {$this->priority->name} by {$this->admin->name}", $this->supportTicket->id, $this->admin->id);
            }

            if ($this->request->status && $this->request->status != $this->supportTicket->ticket_status_id) {
                $this->supportTicket->ticket_status_id = $this->request->status;
                if ($this->admin) {
                    TicketLog::storeLog("Status change to {$this->ticketStatus->name} by {$this->admin->name}", $this->supportTicket->id, $this->admin->id);
                } else {
                    TicketLog::storeLog("Status change to {$this->ticketStatus->name} by customer", $this->supportTicket->id);
                }
            }

            if ($this->admin) {
                TicketLog::storeLog("Ticket replied by {$this->admin->name}", $this->supportTicket->id, $this->admin->id);
            }
            $this->supportTicket->save();
        }
        return true;

    }

    protected function storeSupportAttachments($replyId)
    {
        $path = getFilePath('supportTicket');

        foreach ($this->files as $file) {
            try {
                $size       = formatFileSize($file->getSize());
                $name       = $file->getClientOriginalName();
                $type       = $file->getMimeType();
                $storedPath = fileUploader($file, $path);

                $attachment                          = new SupportTicketAttachment();
                $attachment->support_ticket_reply_id = $replyId;
                $attachment->size                    = $size;
                $attachment->name                    = $name;
                $attachment->type                    = $type;
                $attachment->attachment              = $storedPath;
                $attachment->save();
            } catch (\Exception $exp) {
                return false;
            }
        }

        return 200;
    }

    private function storeAdminNotification()
    {
        $supportTicket = $this->supportTicket;

        $adminNotification            = new AdminNotification();
        $adminNotification->user_id   = $supportTicket->user_id;
        $adminNotification->title     = 'New ticket open via ' . ($this->createdVia == Status::CREATED_VIA_ADMIN ? 'admin' : 'email');
        $adminNotification->click_url = urlPath('admin.ticket.details', $supportTicket->ticket_number);
        $adminNotification->save();
    }

    private function spamFilter()
    {
        $isSpam = SpamFilter::isSpam($this->request->description, subject: $this->request->subject) || SpamFilter::isSpam($this->user->fullname, 'name') || SpamFilter::isSpam($this->user->email, 'email') || SpamFilter::isSpam(getRealIP(), 'ip');

        if ($isSpam) {
            $this->supportTicket->folder = Status::TICKET_SPAM;
            $this->supportTicket->save();
        }
    }

    public function sendMail($reply = false)
    {
        $email = $this->department->pipe_only ? $this->department->email_user : ($this->department->email ?? $this->company->email);

        $sendFromData = [
            'email_address' => $email,
            'name'          => $this->department->name,
        ];

        $supportTicket = $this->supportTicket;
        $templateName  = $reply ? 'TICKET_REPLY' : 'TICKET_ADDED';

        notify($this->user, $templateName, [
            'ticket_subject'  => $supportTicket->subject,
            'ticket_number'   => $supportTicket->ticket_number,
            'ticket_message'  => purifyHtml($this->request->description),
            'department_name' => $this->department->name,
        ], sendFromData: $sendFromData);
    }

}
