<?php namespace App\Http\Controllers\Admin;

use App\Exceptions\ResourseNotFoundException;
use Facades\Repositories\UserRepo;
use Facades\Repositories\DeviceRepo;
use Facades\Repositories\EventRepo;
use Illuminate\Auth\Access\UnauthorizedException;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Redirect;
use Tobuli\Exceptions\ValidationException;
use Tobuli\Exporters\Device\ExporterManager;
use Tobuli\Repositories\User\UserRepositoryInterface as User;
use Tobuli\Validation\ClientFormValidator;
use Tobuli\Repositories\Device\DeviceRepositoryInterface as Device;
use Tobuli\Repositories\TraccarDevice\TraccarDeviceRepositoryInterface as TraccarDevice;
use Tobuli\Repositories\Event\EventRepositoryInterface as Event;
use Validator;

class ObjectsController extends BaseController
{

    /**
     * @var ClientFormValidator
     */
    private $clientFormValidator;

    private $section = 'objects';

    /**
     * @var Device
     */
    private $device;

    /**
     * @var TraccarDevice
     */
    private $traccarDevice;

    /**
     * @var Event
     */
    private $event;

    function __construct(
        ClientFormValidator $clientFormValidator,
        Device $device,
        TraccarDevice $traccarDevice,
        Event $event
    ) {
        parent::__construct();
        $this->clientFormValidator = $clientFormValidator;
        $this->device = $device;
        $this->traccarDevice = $traccarDevice;
        $this->event = $event;
    }

    public function index()
    {
        $input = Input::all();
        $users = null;
        if (Auth::User()->isManager()) {
            $users = Auth::User()->subusers()->lists('id', 'id')->all();
            $users[] = Auth::User()->id;
        }

        $items = $this->device->searchAndPaginateAdmin($input, 'name', 'asc', 10, $users);
        $section = $this->section;
        $page = $items->currentPage();
        $total_pages = $items->lastPage();
        $pagination = smartPaginate($items->currentPage(), $total_pages);
        $url_path = $items->resolveCurrentPath();

        return View::make('admin::' . ucfirst($this->section) . '.' . (Request::ajax() ? 'table' : 'index'))->with(compact('items',
            'input', 'section', 'page', 'total_pages', 'pagination', 'url_path'));
    }

    public function create()
    {
        $managers = ['0' => '-- ' . trans('admin.select') . ' --'] + UserRepo::getOtherManagers(0)->lists('email',
                'id')->all();

        return View::make('admin::' . ucfirst($this->section) . '.create')->with(compact('managers'));
    }

    public function destroy()
    {
        if (config('tobuli.object_delete_pass') && isAdmin() && request('password') != config('tobuli.object_delete_pass')) {
            return ['status' => 0, 'errors' => ['message' => trans('front.login_failed')]];
        }

        $ids = Input::get('ids');

        if (is_array($ids) && count($ids)) {
            foreach ($ids as $id) {
                $item = DeviceRepo::find($id);

                if (empty($item) || ( ! $item->users->contains($this->user->id) && ! isAdmin()))
                    continue;

                beginTransaction();

                try {
                    DB::connection('traccar_mysql')->table('devices')->where('id', '=',
                        $item->traccar_device_id)->delete();
                    EventRepo::deleteWhere(['device_id' => $item->id]);
                    DeviceRepo::delete($item->id);

                    DB::table('user_device_pivot')->where('device_id', $item->id)->delete();
                    DB::table('device_sensors')->where('device_id', $item->id)->delete();
                    DB::table('device_services')->where('device_id', $item->id)->delete();
                    DB::table('user_drivers')->where('device_id', $item->id)->update(['device_id' => null]);

                    if (Schema::connection('traccar_mysql')->hasTable('positions_' . $item->traccar_device_id))
                        DB::connection('traccar_mysql')->table('positions_' . $item->traccar_device_id)->truncate();

                    Schema::connection('traccar_mysql')->dropIfExists('positions_' . $item->traccar_device_id);

                    commitTransaction();
                } catch (\Exception $e) {
                    rollbackTransaction();
                }
            }
        }

        return Response::json(['status' => 1]);
    }

    public function doDestroy()
    {
        return view('admin::Objects.destroy', ['ids' => request('id')]);
    }

    public function restartTraccar()
    {
        $status = 0;
        $res = restartTraccar('user_manual_restart');
        if ($res == 'OK')
            return Redirect::route('admin.clients.index')->withSuccess(trans('admin.tracking_service_restarted'));
        else
            return Redirect::route('admin.clients.index')->withError(trans('admin.' . $res));
    }

    public function import()
    {
        return View::make('admin::' . ucfirst($this->section) . '.import');
    }

    public function importSet()
    {
        $file = Request::file('file');

        if ( ! $file->isValid())
            return;

        try {
            $manager = new \Tobuli\Importers\Device\DeviceImportManager();
            $manager->import($file->getPathName());

            return Response::json(['status' => 1]);
        } catch (ValidationException $e) {
            return Response::json(['errors' => $e->getErrors()]);
        }
    }

    public function export()
    {
        $devices = $this->user->accessibleDevices;

        if ($devices->isEmpty())
            return Response::json(['status' => 1]);

        $fields = request()->get('fields', []);

        foreach ($fields as $index => $field) {
            if ($this->user->can('view', $devices->first(), $field))
                continue;

            unset($fields[$index]);
        }

        $exporter = new ExporterManager($devices, $fields);

        return $exporter->download(request('format'));
    }

    public function exportModal()
    {
        return view('admin::' . ucfirst($this->section) . '.export', [
            'formats' => config('tobuli.exports.formats'),
            'fields'  => \Tobuli\Entities\Device::getFields()
        ]);
    }

    public function expiration(\Illuminate\Http\Request $request, $imei)
    {
        $validator = Validator::make($request->all(), [
            'expiration_date' => 'required|date',
        ]);

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

        $device = $this->device->findWhere(['imei' => $imei]);

        if ( ! $device)
           throw new ResourseNotFoundException('global.device');

        $device->update(['expiration_date' => $request->input('expiration_date')]);

        return Response::json(['status' => 1]);
    }

    public function bulkDelete()
    {
        if ( ! $this->user->isAdmin())
            throw new UnauthorizedException();

        $validator = Validator::make(request()->all(), ['file' => 'required']);

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

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

        if (is_null($file) || $file->getClientOriginalExtension() != 'csv')
            throw new ValidationException('Only CSV');

        $source = file_get_contents($file);
        $rows = str_getcsv($source, "\n");

        if (empty($rows)) {
            return null;
        }
        $headers = array_shift($rows);
        $imeis = $rows;

        $errors_count = 0;
        $content = trans('admin.logs') . " <br>";

        if (is_array($imeis) && count($imeis)) {
            foreach ($imeis as $imei) {
                $device = DeviceRepo::whereImei($imei);

                if (empty($device)) {
                    $content .= trans('validation.attributes.imei') . "($imei) ". trans('global.not_found') ."<br>";
                    $errors_count++;
                    continue;
                }

                if ($this->removeDevice($device) == false) {
                    $content .= trans('global.device') . "($imei) " . trans('global.failed') . "<br>";
                    $errors_count++;
                }
            }
        }

        $content .= "<br>" . trans('global.successful') . " " . lcfirst(trans('global.count')) . ": " . (count($imeis) - $errors_count);
        $content .= "<br>" . trans('global.failed') . " " . lcfirst(trans('global.count')) . ":  $errors_count";

        return Response::json([
            'status'  => 0,
            'content' => $content,
            'trigger' => 'bulk_delete_object',
        ]);
    }

    public function bulkDeleteModal()
    {
        return view('admin::' . ucfirst($this->section) . '.bulk_delete');
    }

    private function removeDevice($device)
    {
        beginTransaction();

        try {
            DB::connection('traccar_mysql')->table('devices')->where('id', '=', $device->traccar_device_id)->delete();
            EventRepo::deleteWhere(['device_id' => $device->id]);
            DeviceRepo::delete($device->id);
            DB::table('user_device_pivot')->where('device_id', $device->id)->delete();
            DB::table('device_sensors')->where('device_id', $device->id)->delete();
            DB::table('device_services')->where('device_id', $device->id)->delete();
            DB::table('user_drivers')->where('device_id', $device->id)->update(['device_id' => null]);
            if (Schema::connection('traccar_mysql')->hasTable('positions_' . $device->traccar_device_id))
                DB::connection('traccar_mysql')->table('positions_' . $device->traccar_device_id)->truncate();
            Schema::connection('traccar_mysql')->dropIfExists('positions_' . $device->traccar_device_id);

            commitTransaction();
        } catch (\Exception $e) {
            rollbackTransaction();
            return false;
        }

        return true;
    }
}
