<?php

namespace App\Http\Controllers\panel;

use App\Department;
use App\Http\Controllers\Controller;
use App\QuickResponse;
use App\Reply;
use App\Staff;
use App\Ticket;
use App\User;
use Carbon\Carbon;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\MediaLibrary\Support\MediaStream;

class TicketController extends Controller
{

    public function __construct()
    {
        $this->authorizeResource(Ticket::class);
    }

    /**
     * Display a listing of the resource.
     *
     * @param Request $request
     * @return Factory|View
     */
    public function index(Request $request)
    {
        $tickets = $this->getTickets($request);
        $users = User::where('role', 'user')->get();
        $departments = Department::all();
        $hasFilter = $request->boolean('filter');

        return view('panel.tickets.list', compact('tickets', 'users', 'departments', 'hasFilter'));
    }

    public function getTickets(Request $request)
    {
        $tickets = new \App\Ticket();
        $perPageCount = 20;
        $orderBy = 'updated_at';
        $order = 'desc';
        $isStaff = auth()->user()->isStaff();

        if ($request->boolean('filter')) {
            if ($request->filled('fdate') and !$request->filled('tdate')) {
                //we will divide the timestamp by 1000 to convert javascript timestamp to php
                $fromDate = Carbon::createFromTimestamp(floor($request->fdate / 1000))->startOfDay();
                $tickets = $tickets->whereDate('created_at', '>=', $fromDate);
            } elseif ($request->filled('tdate') and !$request->filled('fdate')) {
                $toDate = Carbon::createFromTimestamp(floor($request->tdate / 1000))->endOfDay();
                $tickets = $tickets->whereDate('created_at', '<=', $toDate);
            } elseif ($request->filled('tdate') and $request->filled('fdate')) {
                $fromDate = Carbon::createFromTimestamp(floor($request->fdate / 1000))->startOfDay();
                $toDate = Carbon::createFromTimestamp(floor($request->tdate / 1000))->endOfDay();
                $tickets = $tickets->whereBetween('created_at', [$fromDate, $toDate]);
            }

            if (!$isStaff and $request->filled('users')) {
                $request->validate([
                    'users' => 'array',
                    'users.*' => 'exists:users,id'
                ]);
                $tickets = $tickets->whereIn('user_id', $request->users);
            }

            if (!$isStaff and $request->filled('departments')) {
                $request->validate([
                    'departments' => 'array',
                    'departments.*' => 'exists:departments,id'
                ]);
                $tickets = $tickets->whereIn('department_id', $request->departments);
            }

            if ($request->filled('priorities')) {
                $request->validate([
                    'priorities' => 'array',
                    'priorities.*' => 'in:1,2,3,4'
                ]);
                $tickets = $tickets->whereIn('priority', $request->priorities);
            }

            if ($request->filled('statuses')) {
                $request->validate([
                    'statuses' => 'array',
                    'statuses.*' => 'in:replied,read,unread,replied_pv,referred'
                ]);
                $tickets = $tickets->whereIn('status', $request->statuses);
            }

            if ($request->filled('trackId')) {
                $tickets = $tickets->where('track_id', $request->trackId);
            }

            if ($request->openCloseStatus) {
                if ($request->openCloseStatus == 'close') {
                    $tickets = $tickets->whereNotNull('closed_at');
                } elseif ($request->openCloseStatus == 'open') {
                    $tickets = $tickets->whereNull('closed_at');
                }
            }

            if ($request->filled('subject')) {
                $tickets = $tickets->where('subject', 'like', '%' . $request->subject . '%');
            }

            if ($request->filled('perPageCount')) {
                $request->validate([
                    'perPageCount' => 'numeric',
                ]);
                $perPageCount = $request->perPageCount;
            }

            if ($request->filled('orderBy') and $request->orderBy != 'none') {
                $request->validate([
                    'orderBy' => 'in:created_at,updated_at,priority,closed_at',
                ]);
                $orderBy = $request->orderBy;
            }

            if ($request->filled('order') and $request->order != 'none') {
                $request->validate([
                    'order' => 'in:asc,desc,none',
                ]);
                $order = $request->order;
            }
        }

        if ($isStaff) {
            $tickets = $tickets->whereIn('department_id', auth()->user()->department()->pluck('id')->toArray());
        }

        return $tickets->with('department', 'user')->orderBy($orderBy, $order)->paginate($perPageCount);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Factory|View
     */
    public function create()
    {
        if (auth()->user()->isUser()) {
            $departments = Department::where('is_hidden', false)->get();
            return view('panel.tickets.user-add', compact('departments'));
        }
        $departments = auth()->user()->isStaff() ? Staff::find(auth()->id())->departments()->get() : Department::all();
        $users = User::where('role', 'user')->get();
        return view('panel.tickets.add', compact('departments', 'users'));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     * @return JsonResponse|RedirectResponse
     */
    public function store(Request $request)
    {
        $admins = User::where('role', 'admin')->orWhere('role', 'superadmin')->pluck('id')->toArray();
        $staffs = Staff::pluck('id')->toArray();
        $hiddenDepartments = Department::where('is_hidden', true)->pluck('id')->toArray();
        $hiddenDepartmentsRule = auth()->user()->isUser() ? '|not_in:' . implode(',', $hiddenDepartments) : '';
        $userRule = !auth()->user()->isUser() ? [
            'required',
            'exists:users,id',
            Rule::notIn(array_merge($admins, $staffs))
        ] : 'nullable';
        $redirectUrl = !auth()->user()->isUser() ? 'panel.tickets.index' : 'panel.index';

        $request->validate([
            'subject' => 'required|max:255',
            'priority' => 'required|in:1,2,3,4',
            'department' => 'required|exists:departments,id' . $hiddenDepartmentsRule,
            'user' => $userRule,
            'text' => 'required',
        ]);

        $data['subject'] = $request->subject;
        $data['content'] = $request->text;
        $data['priority'] = $request->priority;
        $data['status'] = 'unread';
        $data['user_id'] = $request->user ?: auth()->user()->id;
        $data['department_id'] = $request->department;
        $data['track_id'] = Ticket::generateTrackCode();

        $ticket = Ticket::create($data);

        foreach ($request->input('attach', []) as $index => $file) {
            $ticket
                ->addMedia(storage_path('tmp/uploads/' . $file))
                ->usingFileName($request->input('attachName.' . $index) . '.' . pathinfo($file, PATHINFO_EXTENSION))
                ->toMediaCollection('attach');
        }

        event(new \App\Events\tickets\Created($ticket));

        if ($request->expectsJson()) {
            return response()->json(['status' => 'success', 'msg' => 'با موفقیت ارسال شد']);
        }

        return redirect()->route($redirectUrl)->with('success', 'با موفقیت ارسال شد');
    }


    /**
     * Display the specified resource.
     *
     * @param Ticket $ticket
     * @return Factory|View
     */
    public function show(Ticket $ticket)
    {
        $lastReply = null;
        $quickResponses = QuickResponse::latest()->get();

        if ($ticket->isReplied()) {
            $lastReply = $ticket->replies()->where('is_private', false)->orderBy('id', 'desc')->first();
        }
        if (auth()->user()->isSuperior()) {
            if ($ticket->status == 'unread') {
                $ticket->update(['status' => 'read']);
            }
            $view = 'panel.tickets.show';
        } else {
            $view = 'panel.tickets.user-show';
            $ticket = $ticket->load([
                'replies' => function ($query) {
                    $query->where('is_private', false);
                }
            ], 'user');
        }

        return view($view, compact('ticket', 'lastReply', 'quickResponses'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param Ticket $ticket
     * @return Response
     */
    public function edit(Ticket $ticket)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param Request $request
     * @param Ticket $ticket
     * @return Response
     */
    public function update(Request $request, Ticket $ticket)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param Ticket $ticket
     * @return JsonResponse
     */
    public function destroy(Ticket $ticket)
    {
        $ticket->delete();
        return response()->json(['status' => 'success', 'msg' => 'با موفقیت حذف شد']);
    }

    /**
     * Change status of the specified ticket to closed
     *
     * @param Ticket $ticket
     * @param Request $request
     * @return JsonResponse|RedirectResponse
     */
    public function close(Ticket $ticket, Request $request)
    {
        if (auth()->user()->isUser() and !config('can_user_close_ticket')) {
            if ($request->expectsJson()) {
                return response()->json(['status' => 'fail', 'msg' => 'شما مجوز بستن تیکت را ندارید']);
            }

            return redirect()->route('panel.tickets.index')->with('success', 'شما مجوز بستن تیکت را ندارید');
        }

        $ticket->closed_at = Carbon::now();
        $ticket->save();

        if ($request->expectsJson()) {
            return response()->json(['status' => 'success', 'msg' => 'وضعیت تیکت به بسته تغییر پیدا کرد']);
        }

        return redirect()->route('panel.tickets.index')->with('success', 'وضعیت تیکت به بسته تغییر پیدا کرد');
    }

    /**
     * Reopens the ticket
     *
     * @param Ticket $ticket
     * @param Request $request
     * @return JsonResponse|RedirectResponse
     */
    public function reopen(Ticket $ticket, Request $request)
    {
        if (auth()->user()->isUser() and !config('can_user_reopen_ticket')) {
            if ($request->expectsJson()) {
                return response()->json(['status' => 'fail', 'msg' => 'شما مجوز بازگشایی تیکت را ندارید']);
            }

            return redirect()->route('panel.tickets.index')->with('success', 'شما مجوز بازگشایی تیکت را ندارید');
        }

        $ticket->closed_at = null;
        $ticket->save();

        if ($request->expectsJson()) {
            return response()->json(['status' => 'success', 'msg' => 'تیکت با موفقیت بازگشایی شد']);
        }

        return redirect()->route('panel.tickets.index')->with('success', 'تیکت با موفقیت بازگشایی شد');
    }

    /**
     * Refers the ticket to selected department
     *
     * @param Ticket $ticket
     * @param Request $request
     * @return JsonResponse|RedirectResponse
     */
    public function refer(Ticket $ticket, Request $request)
    {
        if (auth()->user()->isUser() and !config('can_user_change_department')) {
            if ($request->expectsJson()) {
                return response()->json(['status' => 'fail', 'msg' => 'شما مجوز تغییر دپارتمان را ندارید']);
            }

            return redirect()->route('panel.tickets.index')->with('success', 'شما مجوز تغییر دپارتمان را ندارید');
        }


        $request->validate([
            'department' => 'required|exists:departments,id'
        ]);

        $ticket->department_id = $request->department;
        $ticket->status = 'referred';
        $ticket->save();

        $msg = ' تیکت با موفقیت به دپارتمان ' . Department::findOrFail($request->department)->name . ' ارجاع داده شد';

        if ($request->expectsJson()) {
            return response()->json(['status' => 'success', 'msg' => $msg]);
        }

        return redirect()->route('panel.tickets.index')->with('success', $msg);
    }

    /**
     * Rates the reply
     *
     * @param Request $request
     * @param Reply $reply
     * @return bool|JsonResponse
     */
    public function addRateToReply(Request $request, Reply $reply)
    {
        $request->validate([
            'rate' => 'required|min:1|max:5'
        ]);

        if (!$reply->isRated() and auth()->user()->isUser()) {
            $reply->rate = $request->rate;
            $reply->save();

            return response()->json(['status' => 'success']);
        }

        return false;
    }

    /**
     * Stores Files so it can be used later
     *
     * @param Request $request
     * @param Reply $reply
     * @return bool|JsonResponse
     */
    public function storeFile(Request $request)
    {
        $allowedMimeTypes = str_replace('.', '', config('upload_allowed_formats'));
        $request->validate([
            'file' => "mimes:$allowedMimeTypes"
        ]);

        $path = storage_path('tmp/uploads');

        if (!file_exists($path)) {
            mkdir($path, 0777, true);
        }

        $file = $request->file('file');

        $extension = $file->extension() ?: $file->getExtension();

        $name = Str::random(64) . '.' . $extension;

        $file->move($path, $name);

        return response()->json([
            'name' => $name,
            'original_name' => htmlentities(trim(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME))),
            'id' => Str::random()
        ]);
    }

    public function downloadAttach(Ticket $ticket, Media $media)
    {
        $this->authorize('downloadAttach', $ticket);
        return response()->download($media->getPath(), $media->file_name);
    }

    public function downloadAllAttaches(Ticket $ticket)
    {
        $this->authorize('downloadAttach', $ticket);
        $downloads = $ticket->getMedia('attach');
        return MediaStream::create("ticket-attaches-{$ticket->id}.zip")->addMedia($downloads);
    }

    /**
     * Add a reply to the ticket
     *
     * @param Request $request
     * @param Ticket $ticket
     * @return JsonResponse|RedirectResponse
     */
    public function reply(Request $request, Ticket $ticket)
    {
        if ($ticket->isClosed() and auth()->user()->isUser()) {
            if ($request->expectsJson()) {
                return response()->json(['status' => 'fail', 'msg' => 'امکان پاسخ به تیکت بسته وجود ندارد']);
            }

            return redirect()->route('panel.tickets.index')->with('success', 'امکان پاسخ به تیکت بسته وجود ندارد');
        }

        $request->validate([
            'text' => 'required'
        ]);

        $reply = $ticket->replies()->create([
            'replied_by' => auth()->user()->id,
            'content' => $request->text,
            'is_private' => $request->is_private ?? false
        ]);

        foreach ($request->input('attach', []) as $index => $file) {
            $reply->addMedia(storage_path('tmp/uploads/' . $file))
                ->usingFileName($request->input('attachName.' . $index) . '.' . pathinfo($file, PATHINFO_EXTENSION))
                ->toMediaCollection('attach');
        }

        if (auth()->user()->isSuperior()) {
            if ($request->has('is_private')) {
                $ticket->status = 'replied_pv';
                event(new \App\Events\tickets\SuperiorRepliedPrivate($ticket, $reply));
            } else {
                $ticket->status = 'replied';
                event(new \App\Events\tickets\SuperiorReplied($ticket, $reply));
            }
        } else {
            $ticket->status = 'unread';
            event(new \App\Events\tickets\UserReplied($ticket, $reply));
        }

        // update ticket status
        $ticket->save();


        if ($request->expectsJson()) {
            return response()->json(['status' => 'success', 'msg' => 'پاسخ با موفقیت ارسال شد']);
        }

        return redirect()->route('panel.tickets.index')->with('success', 'پاسخ با موفقیت ارسال شد');
    }

    /**
     * Change the status of ticket to unread
     *
     * @param Request $request
     * @param Ticket $ticket
     * @return JsonResponse|RedirectResponse
     */
    public function markAsUnread(Request $request, Ticket $ticket)
    {
        if (!auth()->user()->isUser()) {
            $ticket->status = 'unread';
            $ticket->save();

            if ($request->expectsJson()) {
                return response()->json(['status' => 'success', 'msg' => 'وضعیت تیکت به خوانده نشده تغییر پیدا کرد']);
            }

            return redirect()->route('panel.tickets.index')->with(
                'success',
                'وضعیت تیکت به خوانده نشده تغییر پیدا کرد'
            );
        }
    }
}
