<?php

namespace App\Http\Controllers;

use App\Models\Restaurante;
use App\Models\Usuario;
use App\Services\Auth0Service;
use App\Services\PaginatorService;
use App\Services\UuidService;
use App\Exceptions\GenericException;
use App\Exceptions\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Ramsey\Uuid\Uuid;

class UsuariosController extends Controller {

    /**
     * Obtiene de forma paginada los usuarios registrados en el backend
     */
    public function all(Request $request) {
        $usuarios = $request->user->colegas();

        $paginate = app(PaginatorService::class)->paginate(
            perPage: $request->input('per_page', 15),
            page: $request->input('page', 1),
            total: $usuarios->count(),
            route: 'users.all',
        );

        $data = $usuarios
            ->skip($paginate['from'] - 1)
            ->take($paginate['per_page'])->all();

        return response()->json([
            'pagination' => $paginate,
            'data' => $data
        ]);
    }

    /**
     * Obtiene un usuario por su id
     */
    public function get(Request $request, $id) {
        if (!str_starts_with($id, 'auth0') && $id != 'me') app(UuidService::class)->validOrFail($id);

        if ($id == 'me') $usuario = $request->user;
        else $usuario = Usuario::findOrFail(urldecode($id));

        return response()->json($usuario);
    }

    /**
     * Crea un nuevo usuario localmente y en auth0
     */
    public function create(Request $request) {
        $this->validate($request, [
            'nombre' => 'required',
            'email' => 'required|email',
            'username' => 'required',
            'password' => 'required',
            'roles' => 'required|array',
            'roles.*' => ['required', Rule::in(['admin', 'mesero', 'recaudador', 'productor'])],
            'restaurant' => 'required|exists:restaurantes,id',
        ]);

        $restaurant = Restaurante::findOrFail($request->input('restaurant'));

        $auth0 = app(Auth0Service::class);
        $auth0User = $auth0->createUser(
            email: $request->input('email'),
            username: $request->input('username'),
            password: $request->input('password'),
            metadata: [
                'roles' => $request->input('roles'),
                'restaurantes' => [$restaurant->id],
            ]
        );

        if (array_key_exists('error', $auth0User)) {
            return response()->json([
                'error' => $auth0User['errorCode'],
                'message' => $auth0User['message'],
            ], $auth0User['statusCode']);
        }

        $usuario = $restaurant->usuarios()->create([
            'id' => Uuid::uuid4(),
            'auth0_id' => $auth0User['identities'][0]['provider'] . '|' . $auth0User['identities'][0]['user_id'],
            'nombre' => $request->input('nombre')
        ]);

        return response()->json($usuario, 201);
    }

    /**
     * Actualiza un usuario
     */
    public function update(Request $request, $id) {
        if (!str_starts_with($id, 'auth0') && $id != 'me') app(UuidService::class)->validOrFail($id);

        $this->validate($request, [
            'nombre' => 'sometimes',
            'email' => 'sometimes|email',
            'username' => 'sometimes',
            'password' => 'sometimes',
            'roles' => 'sometimes|array',
            'roles.*' => ['sometimes', Rule::in(['admin', 'mesero', 'recaudador', 'productor'])],
        ]);

        if ($id == 'me') $usuario = $request->user;
        else $usuario = Usuario::findOrFail(urldecode($id));

        $metadata = [];
        if ($request->input('roles')) $metadata['roles'] = $request->input('roles');

        $auth0 = app(Auth0Service::class);
        $auth0User = $auth0->updateUser(
            auth0_id: $usuario->auth0_id,
            email: $request->input('email'),
            username: $request->input('username'),
            password: $request->input('password'),
            metadata: $metadata
        );

        if (array_key_exists('error', $auth0User)) {
            return response()->json([
                'error' => $auth0User['errorCode'],
                'message' => $auth0User['message'],
            ], $auth0User['statusCode']);
        }

        if ($request->input('nombre')) $usuario->nombre = $request->input('nombre');
        $usuario->save();

        return response()->json($usuario);
    }

    /**
     * Elimina un usuario
     */
    public function delete(Request $request, $id) {
        if (!str_starts_with($id, 'auth0') && $id != 'me') app(UuidService::class)->validOrFail($id);

        if ($id == 'me') $usuario = $request->user;
        else $usuario = Usuario::findOrFail(urldecode($id));

        $auth0 = app(Auth0Service::class);
        $auth0Response = $auth0->deleteUser($usuario->auth0_id);

        if ($auth0Response && array_key_exists('error', $auth0Response)) {
            return response()->json([
                'error' => $auth0Response['errorCode'],
                'message' => $auth0Response['message'],
            ], $auth0Response['statusCode']);
        }

        $usuario->delete();

        return response()->json([], 204);
    }

    /**
     * Obtiene los usuarios de un restaurant
     */
    public function getRestaurantes(Request $request, $id) {
        if (!str_starts_with($id, 'auth0') && $id != 'me') app(UuidService::class)->validOrFail($id);

        if ($id == 'me') $usuario = $request->user;
        else $usuario = Usuario::findOrFail(urldecode($id));

        return response()->json($usuario->restaurantes);
    }

    /**
     * Agrega usuario a un restaurant
     */
    public function addToRestaurant(Request $request, $id, $restaurant) {
        if (!str_starts_with($id, 'auth0') && $id != 'me') app(UuidService::class)->validOrFail($id);
        app(UuidService::class)->validOrFail($restaurant);

        if ($id == 'me') $usuario = $request->user;
        else $usuario = Usuario::findOrFail(urldecode($id));

        $restaurant = Restaurante::findOrFail($restaurant);

        if ($usuario->restaurantes->contains($restaurant)) {
            return response()->json([
                'error' => 'already_on_restaurant',
                'message' => 'El usuario ' . $usuario->id . ' ya se encuentra en el restaurante ' . $restaurant->id
            ], 400);
        }

        $restaurant->usuarios()->attach($usuario);

        return response()->json($usuario->fresh(['restaurantes']));
    }

    /**
     * Saca a un usuario de un restaurant
     */
    public function removeFromRestaurant(Request $request, $id, $restaurant) {
        if (!str_starts_with($id, 'auth0') && $id != 'me') app(UuidService::class)->validOrFail($id);
        app(UuidService::class)->validOrFail($restaurant);

        if ($id == 'me') $usuario = $request->user;
        else $usuario = Usuario::findOrFail(urldecode($id));

        $restaurant = Restaurante::findOrFail($restaurant);

        if (!$usuario->restaurantes->contains($restaurant)) {
            return response()->json([
                'error' => 'already_not_on_restaurant',
                'message' => 'El usuario ' . $usuario->id . ' no encuentra en el restaurante ' . $restaurant->id
            ], 400);
        }

        $restaurant->usuarios()->detach($usuario);

        return response()->json($usuario->fresh(['restaurantes']));
    }
}
