<?php

namespace App\Http\Controllers;

use App\Models\Domain;
use App\Http\Requests\StoreLinkRequest;
use App\Http\Requests\UpdateLinkRequest;
use App\Models\Link;
use App\Models\LinkPixel;
use App\Models\Pixel;
use App\Models\Space;
use App\Traits\LinkTrait;
use Carbon\Carbon;
use Carbon\CarbonTimeZone;
use Illuminate\Http\Request;
use League\Csv as CSV;

class LinkController extends Controller
{
    use LinkTrait;

    /**
     * List the Links.
     *
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function index(Request $request)
    {
        // Get the user's spaces
        $spaces = Space::where('user_id', $request->user()->id)->get();

        // Get the user's domains
        $domains = Domain::whereIn('user_id', [0, $request->user()->id])->orderBy('name')->get();

        // Get the user's pixels
        $pixels = Pixel::where('user_id', $request->user()->id)->get();

        $search = $request->input('search');
        $searchBy = in_array($request->input('search_by'), ['title', 'alias', 'url']) ? $request->input('search_by') : 'title';
        $spaceId = $request->input('space_id');
        $domainId = $request->input('domain_id');
        $pixelId = $request->input('pixel_id');
        $status = $request->input('status');
        $sortBy = in_array($request->input('sort_by'), ['id', 'clicks', 'title', 'alias', 'url']) ? $request->input('sort_by') : 'id';
        $sort = in_array($request->input('sort'), ['asc', 'desc']) ? $request->input('sort') : 'desc';
        $perPage = in_array($request->input('per_page'), [10, 25, 50, 100]) ? $request->input('per_page') : config('settings.paginate');

        // If there's no toast notification
        if ($request->session()->get('toast') == false) {
            // Set the session to a countable object
            $request->session()->put('toast', []);
        }

        $links = Link::with('domain', 'space')
            ->where('user_id', $request->user()->id)
            ->when($domainId, function ($query) use ($domainId) {
                return $query->ofDomain($domainId);
            })
            ->when(isset($spaceId) && is_numeric($spaceId), function ($query) use ($spaceId) {
                return $query->ofSpace($spaceId);
            })
            ->when($pixelId, function ($query) use ($pixelId) {
                return $query->whereIn('id', LinkPixel::select('link_id')->where('pixel_id', '=', $pixelId)->get());
            })
            ->when($status, function ($query) use ($status) {
                if($status == 1) {
                    return $query->active();
                } elseif($status == 2) {
                    return $query->expired();
                }
            })
            ->when($search, function ($query) use ($search, $searchBy) {
                if($searchBy == 'url') {
                    return $query->searchUrl($search);
                } elseif ($searchBy == 'alias') {
                    return $query->searchAlias($search);
                }
                return $query->searchTitle($search);
            })
            ->orderBy($sortBy, $sort)
            ->paginate($perPage)
            ->appends(['search' => $search, 'search_by' => $searchBy, 'domain_id' => $domainId, 'space_id' => $spaceId, 'pixel_id' => $pixelId, 'sort_by' => $sortBy, 'sort' => $sort, 'per_page' => $perPage]);

        return view('links.container', ['view' => 'list', 'links' => $links, 'spaces' => $spaces, 'domains' => $domains, 'pixels' => $pixels]);
    }

    /**
     * Export the Links.
     *
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
     * @throws CSV\CannotInsertRecord
     */
    public function export(Request $request)
    {
        if ($request->user()->cannot('dataExport', ['App\Models\User'])) {
            abort(403);
        }

        $search = $request->input('search');
        $spaceId = $request->input('space_id');
        $domainId = $request->input('domain_id');
        $pixelId = $request->input('pixel_id');
        $status = $request->input('status');
        $by = $request->input('by');

        if ($request->input('sort') == 'min') {
            $sort = ['clicks', 'asc'];
        } elseif ($request->input('sort') == 'max') {
            $sort = ['clicks', 'desc'];
        } elseif ($request->input('sort') == 'asc') {
            $sort = ['id', 'asc'];
        } else {
            $sort = ['id', 'desc'];
        }

        $links = Link::with('domain', 'space')
            ->where('user_id', $request->user()->id)
            ->when($domainId, function ($query) use ($domainId) {
                return $query->ofDomain($domainId);
            })
            ->when(isset($spaceId) && is_numeric($spaceId), function ($query) use ($spaceId) {
                return $query->ofSpace($spaceId);
            })
            ->when($pixelId, function ($query) use ($pixelId) {
                return $query->whereIn('id', LinkPixel::select('link_id')->where('pixel_id', '=', $pixelId)->get());
            })
            ->when($status, function ($query) use ($status) {
                if($status == 1) {
                    return $query->active();
                } elseif($status == 2) {
                    return $query->expired();
                }
            })
            ->when($search, function ($query) use ($search, $by) {
                if($by == 'url') {
                    return $query->searchUrl($search);

                } elseif ($by == 'alias') {
                    return $query->searchAlias($search);
                }
                return $query->searchTitle($search);
            })
            ->orderBy($sort[0], $sort[1])
            ->get();

        $content = CSV\Writer::createFromFileObject(new \SplTempFileObject);

        // Generate the header
        $content->insertOne([__('Type'), __('Links')]);
        $content->insertOne([__('Date'), Carbon::now()->tz($request->user()->timezone ?? config('settings.timezone'))->format(__('Y-m-d')) . ' ' . Carbon::now()->tz($request->user()->timezone ?? config('settings.timezone'))->format('H:i:s') . ' (' . CarbonTimeZone::create($request->user()->timezone ?? config('settings.timezone'))->toOffsetName() . ')']);
        $content->insertOne([__('URL'), $request->fullUrl()]);
        $content->insertOne([__(' ')]);

        // Generate the content
        $content->insertOne([__('Short'), __('Original'), __('Alias'), __('Title'), __('Created at')]);
        foreach ($links as $link) {
            $content->insertOne([($link->shortUrl), $link->url, $link->alias, $link->title, $link->created_at->tz($request->user()->timezone ?? config('settings.timezone'))->format(__('Y-m-d'))]);
        }

        // Set the output BOM
        $content->setOutputBOM(CSV\Reader::BOM_UTF8);

        return response((string) $content, 200, [
            'Content-Type' => 'text/csv',
            'Content-Transfer-Encoding' => 'binary',
            'Content-Disposition' => 'attachment; filename="' . formatTitle([__('Links'), config('settings.title')]) . '.csv"',
        ]);
    }

    /**
     * Show the edit Link form.
     *
     * @param Request $request
     * @param $id
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function edit(Request $request, $id)
    {
        $link = Link::where([['id', '=', $id], ['user_id', '=', $request->user()->id]])->firstOrFail();

        $spaces = Space::where('user_id', $request->user()->id)->get();

        $domains = Domain::whereIn('user_id', [0, $request->user()->id])->get();

        $pixels = Pixel::where('user_id', $request->user()->id)->get();

        return view('links.container', ['view' => 'edit', 'spaces' => $spaces, 'domains' => $domains, 'pixels' => $pixels, 'link' => $link]);
    }

    /**
     * Store the Link.
     * 
     * @param StoreLinkRequest $request
     * @return \Illuminate\Http\RedirectResponse
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function store(StoreLinkRequest $request)
    {
        if ($request->multiple_links) {
            $links = $this->linksStore($request);

            return redirect()->back()->with('toast', Link::where('user_id', '=', $request->user()->id)->orderBy('id', 'desc')->limit(count($links))->get());
        } else {
            $this->linkStore($request);

            return redirect()->back()->with('toast', Link::where('user_id', '=', $request->user()->id)->orderBy('id', 'desc')->limit(1)->get());
        }
    }

    /**
     * Update the Link.
     * 
     * @param UpdateLinkRequest $request
     * @param $id
     * @return \Illuminate\Http\RedirectResponse
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function update(UpdateLinkRequest $request, $id)
    {
        $link = Link::where([['id', '=', $id], ['user_id', '=', $request->user()->id]])->firstOrFail();

        $this->linkUpdate($request, $link);

        return redirect()->route('links.edit', $id)->with('success', __('Settings saved.'));
    }

    /**
     * Delete the Link.
     * 
     * @param Request $request
     * @param $id
     * @return \Illuminate\Http\RedirectResponse
     * @throws \Exception
     */
    public function destroy(Request $request, $id)
    {
        if ($request->has('bulk')) {
            Link::where('user_id', '=', $request->user()->id)->whereIn('id', array_slice(json_decode($id, true), 0, 100))->each(function ($link) use ($request) {
                $link->delete();
            });

            return redirect()->route('links')->with('success', __(':count records have been deleted.', ['count' => $request->input('bulk')]));
        }

        $link = Link::where([['id', '=', $id], ['user_id', '=', $request->user()->id]])->firstOrFail();

        $link->delete();

        return redirect()->route('links')->with('success', __(':name has been deleted.', ['name' => $link->displayShortUrl]));
    }
}
