<?php

namespace App\Http\Controllers\Api;

use Closure;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Modules\Announcement\Entities\Device;
use Symfony\Component\HttpFoundation\Response;

class ApiController extends Controller
{
    protected $headers = ['Content-Type' => 'application/json'];

    public function handleHeaders(Request $request, Closure $next)
    {
        if (app()->isDownForMaintenance() || config('app.maintenance_mode_status') === 'enable') {
            return $this->jsonError(Response::HTTP_SERVICE_UNAVAILABLE, config('app.maintenance_mode_message') ?: __('Server Under Maintenance!'));
        } else if ($request->isMethod('POST') && (empty($request->header('Content-Type')) || $request->header('Content-Type') != 'application/json')) {
            return $this->jsonError(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, 'The value of header \'Content-Type\' must be exactly \'application/json\' only');
        } else if ($request->header('Accept') && !in_array($request->header('Accept'), ['application/json'])) {
            return $this->jsonError(Response::HTTP_NOT_ACCEPTABLE, 'The value of header \'Accept\' must be exactly \'application/json\' only');
        // } else if (empty($request->header('Authorization'))) {
            // return $this->jsonError(Response::HTTP_UNAUTHORIZED, 'The value of header \'Authorization\' is required');
        } else if (empty($request->header('X-Client-Id'))) {
            return $this->jsonError(Response::HTTP_FORBIDDEN, 'The value of header \'X-Client-Id\' is required');
        } else if (empty($request->header('X-Device-Id'))) {
            return $this->jsonError(Response::HTTP_FORBIDDEN, 'The value of header \'X-Device-Id\' is required');
        }

        return $next($request);
    }

    public function handleRestrictIP(Request $request, Closure $next)
    {
        $encrypted_id = $request->header('X-Client-Id');
        $device_id = $request->header('X-Device-Id');

        $generated_id = hash('sha256', $device_id . $request->getContent());

        if ($generated_id != $encrypted_id) {
            return $this->jsonError(Response::HTTP_FORBIDDEN, 'Invalid \'X-Client-Id\' value');
        }

        try {
            if (!in_array($ip = $request->ip(), Device::where('token', $device_id)->pluck('ip_address')->toArray())) {
                // Device::updateOrCreate(['token' => $device_id], ['ip_address' => $ip]);
                return $this->jsonError(Response::HTTP_FORBIDDEN, 'IP address not allowed');
            }
        } catch (\Exception $e) {
            report($e);
        }

        return $next($request);
    }

    public function handleUnauthenticated()
    {
        return $this->jsonError(Response::HTTP_UNAUTHORIZED, 'Unauthorized! Invalid \'Bearer\' token');
    }

    public function handleNotAllowed()
    {
        return $this->jsonError(Response::HTTP_FORBIDDEN, 'Access denied! Permission required');
    }

    public function json(array $data = [], int $status_code = Response::HTTP_OK, array $errors = [], mixed $extra = null): JsonResponse
    {
        $response = [
            'meta' => [
                'endpoint' => url('/'),
                'uri' => request()->path(),
                'copyright' => '© ' . date('Y') . ' ' . config('app.name') . ' ' . __('All rights reserved.'),
                'version' => config('app.version'),
            ],
            'data' => isset($data['data']) ? $data['data'] : $data,
            'extra' => $extra,
            'errors' => $errors,
        ];

        return response()->json($response, $status_code, $this->headers);
    }

    public function jsonError(int $status_code = Response::HTTP_FORBIDDEN, ?string $title = null, array $validations = [], ?string $reference_code = null): JsonResponse
    {
        $errors = [$this->getErrorObject($status_code)];

        $data = [
            // 'type' => 'error',
            'message' => $title ?: Response::$statusTexts[$status_code],
        ];

        if (count($validations) && $status_code == Response::HTTP_UNPROCESSABLE_ENTITY) {
            $data['validations'] = $validations;
        }

        if ($reference_code) {
            $errors[0]['code'] = $reference_code ?: 'G001';
        }

        return $this->json($data, $status_code, $errors);
    }

    public function getErrorObject(int $status_code)
    {
        return [
            'id' => time(),
            'status' => $status_code,
            'title' => Response::$statusTexts[$status_code],
        ];
    }

    public function addValidationErrorCode()
    {
        return [
            'required' => trans('validation.required') . '|V001',
            'string' => trans('validation.string') . '|V002',
            'integer' => trans('validation.integer') . '|V002',
            'alpha_dash' => trans('validation.alpha_dash') . '|V002',
            'unique' => trans('validation.unique') . '|V003',
            'min' => trans('validation.min.numeric') . '|V004',
            'max' => trans('validation.max.numeric') . '|V005',
            'exists' => trans('validation.exists') . '|V006',
            'email' => trans('validation.email') . '|V007',
            'boolean' => trans('validation.boolean') . '|V008',
        ];
    }

    public function handleValidationResponse(array $validations)
    {
        $title = __('The given data was invalid.');
        $array = [];
        $count = 0;

        foreach ($validations as $key => $messages) {
            $details = [];
            $codes = [];

            foreach ($messages as $message) {
                $detail = $message;
                $reference = '';

                if (strpos($message, '|')) {
                    list($detail, $reference) = explode('|', $message);
                }

                $codes[] = $reference;
                $details[] = $detail;

                if (!$count) {
                    $title = $detail;
                }

                $count++;
            }

            $array[] = [
                'key' => $key,
                'codes' => $codes,
                'messages' => $details,
            ];
        }

        if ($count > 1) {
            $count--;
            $pluralized = $count === 1 ? 'error' : 'errors';
            $title .= ' ' . __("(and :count more :pluralized)", ['count' => $count, 'pluralized' => $pluralized]);
        }

        return $this->jsonError(Response::HTTP_UNPROCESSABLE_ENTITY, $title, validations: $array);
    }
}
