<?php

namespace App\Http\Controllers\Api;

use App\Exports\UsersExport;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\EmailVerification;
use App\Http\Requests\Api\RequestChangePassword;
use App\Http\Requests\Api\RequestProfileImage;
use App\Http\Requests\Api\UserCheckDocumentList;
use App\Http\Requests\Api\UserDelete;
use App\Http\Requests\Api\UserFrontendEmailCheck;
use App\Http\Requests\Api\UserIdShow;
use App\Http\Requests\Api\UserFrontendLogin;
use App\Http\Requests\Api\UserFrontendPut;
use App\Http\Requests\Api\UserFrontendResetPassword;
use App\Http\Requests\Api\UserFrontendWorkGroup;
use App\Http\Requests\Api\UserIndex;
use App\Http\Requests\Api\UserJoinDocumentList;
use App\Http\Requests\UserFrontendStore;
use App\Http\Resources\AccountDeletionResource;
use App\Http\Resources\CommitteeWithWorkGroupsListResource;
use App\Http\Resources\UserCommitteesResourceList;
use App\Http\Resources\UserResource;
use App\Http\Resources\UserResourceList;
use App\Http\Resources\WorkGroupSimpleResource;
use App\Libs\Services\BackendUserService;
use App\Libs\Services\UserFrontendService;
use App\Models\Committee;
use App\Models\DocumentList;
use App\Models\Domain;
use App\Models\Member;
use App\Models\User;
use App\Models\WorkGroup;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Log;

class UserController extends Controller
{
    /**
     * Sanitize filename by removing or replacing invalid characters
     *
     * @param string $filename
     * @return string
     */
    private function sanitizeFilename($filename)
    {
        // Remove or replace invalid characters for filenames
        $filename = str_replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename);
        // Remove multiple spaces and trim
        $filename = preg_replace('/\s+/', ' ', trim($filename));
        // Remove leading/trailing dots and spaces
        $filename = trim($filename, '. ');
        return $filename;
    }

    public function postCreate(UserFrontendStore $request, UserFrontendService $userFrontendService, BackendUserService $backendUserService)
    {
        $data = $request->validated();

        if ($data['acceptedTerms'] != "1") {
            return response()->json(null, 404);
        }

        $email = $data['email'];
        $emailParts = explode('@', $email);

        if (count($emailParts) !== 2) {
            return response()->json(null, 404);
        }

        $domain = $emailParts[1];
        $domainData = Domain::where('domain', $domain)->first();

        if (empty($domainData)) {
            return response()->json(
                [
                    "message" => "The email domain is not found.",
                    "errors" => [
                        "email" => "The email domain is incorrect.",
                    ],
                ]
                , 404);
        }

        $data['memberId'] = $domainData->member_id;
        $member = Member::find($data['memberId']);

        if ($member->state !== 'active') {
            return response()->json(
                [
                    "message" => "The member is not active.",
                    "error" => "The member is not active.",
                ]
                , 404);
        }

        $data['state'] = "pendingEmailValidation";

        $user = $userFrontendService->createUser($data);

        if (empty($user)) {
            return response(['error' => 'Wrong Credentials'], 401);
        }

        // $credentials = $request->only('email', 'password');

        // if (!Auth::attempt($credentials)) {
        //     return response(['error' => 'Wrong Credentials'], 401);
        // }

        event(new Registered($user));
        return new UserResource($user);
    }

    public function postLogin(UserFrontendLogin $request)
    {
        $credentials = $request->only('email', 'password');

        // Verify credentials using Hash::check instead of attempt
        $user = User::where('email', $credentials['email'])
            ->with(['member'])
            ->first();

        if (!$user || !Hash::check($credentials['password'], $user->password)) {
            return response(['error' => 'Wrong Credentials'], 401);
        }

        if ($user->state === 'inactive' || $user->member->state !== 'active') {
            return response(['error' => 'Contact Admin'], 401);
        }

        if ($user->state === 'pendingEmailValidation') {
            return response(['error' => 'You need to verify your email'], 401);
        }

        if ($user->state === 'pendingApproval') {
            return response(['error' => 'Your registration is pending approval by Energy Traders Europe'], 401);
        }

        // Create access token with Passport
        $tokenResult = $user->createToken('API Token');
        $token = $tokenResult->accessToken;
        $expiration = $tokenResult->token->expires_at;

        return response()->json([
            'user' => new UserResource($user),
            'token' => $token,
            'token_type' => 'Bearer',
            'expires_at' => $expiration
        ]);
    }

    public function postLogout(Request $request)
    {
        /** @var User $user */
        $user = Auth::user();
        if ($user) {
            // Revoke the user's current token
            $user->token()->revoke();
        }

        return response(['message' => 'Logout successful']);
    }

    public function getIndex(UserIndex $request, UserFrontendService $userFrontendService)
    {
        $data = $request->validated();
        $usersData = $userFrontendService->list($data);

        if ($usersData) {
            return [
                'data' => !empty($data['showCommitteesWorkgroups']) ? UserCommitteesResourceList::collection($usersData['users']) : UserResourceList::collection($usersData['users']),
                'pagination' => $usersData['pagination'],
            ];
        }
        return response()->json([], 200);
    }

    public function uploadProfileImage(RequestProfileImage $request, UserFrontendService $userFrontendService)
    {
        $userData = Auth::user();
        $data = $request->validated();
        $profileImage = $userFrontendService->storeProfileImage($data, $userData);

        if (empty($profileImage)) {
            return response()->json(['message' => 'Failed save profile image'], 404);
        }

        return response()->json(['imageLinks' => getProfileImage($profileImage, ['100x100'])]);
    }

    public function postChangePassword(RequestChangePassword $request, UserFrontendService $userFrontendService)
    {
        $userData = Auth::user();

        if(empty($userData)) {
            abort(404);
        }

        $data = $request->validated();
        $response = $userFrontendService->changePassword($data, $userData);

        if ($response['success']) {
            return response()->json(['message' => $response['message']], 200);
        } else {
            return response()->json(['message' => $response['message']], 422);
        }
    }

    public function getUser()
    {
        $userId = Auth::id();
        $userData = User::where('id', $userId)
            ->with([
                'member' => function ($query) {
                    $query->where('state', 'active');
                },
            ])
            ->first();

        if ($userData) {
            return new UserResource($userData);
        }

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

    public function putUser(UserFrontendPut $request, UserFrontendService $userFrontendService, $userId)
    {
        if(empty($userId)){
            abort(404);
        }

        $data = $request->validated();
        $data['id'] = $userId;
        $userData = $userFrontendService->updateUser($data);

        if ($userData) {
            return new UserResource($userData);
        }
    }

    public function postExport(UserIndex $request)
    {
        // Increase execution time and memory limits for large datasets
        ini_set('max_execution_time', 600); // 10 minutes
        ini_set('memory_limit', '1024M'); // 1GB

        // Force garbage collection and clear any potential memory issues
        if (function_exists('gc_collect_cycles')) {
            gc_collect_cycles();
        }

        $data = $request->validated();
        $name = 'users';
        $ext = '.xlsx';

        if (!empty($data['committeeId'])) {
            $committee = Committee::find($data['committeeId']);
            if ($committee) {
                $name = $name . ' ' . $this->sanitizeFilename($committee->title);
            }
        } else if (!empty($data['workGroupId'])) {
            $wg = WorkGroup::find($data['workGroupId']);
            if ($wg) {
                $name = $name . ' ' . $this->sanitizeFilename($wg->title);
            }
        } else if (!empty($data['documentListId'])) {
            $documentList = DocumentList::find($data['documentListId']);
            if ($documentList) {
                $name = $name . ' ' . $this->sanitizeFilename($documentList->title);
            }
        }

        // Final sanitization of the complete filename
        $name = $this->sanitizeFilename($name);

        try {
            // Estimate dataset size for monitoring
            $userQuery = User::query();

            // Apply same filters as UsersExport to estimate count
            if (!empty($data['committeeId'])) {
                $userQuery->whereHas('committees', function($q) use ($data) {
                    $q->where('committees.id', $data['committeeId']);
                });
            }
            if (!empty($data['workGroupId'])) {
                $userQuery->whereHas('workGroups', function($q) use ($data) {
                    $q->where('work_groups.id', $data['workGroupId']);
                });
            }
            if (!empty($data['documentListId'])) {
                $userQuery->whereHas('documentLists', function($q) use ($data) {
                    $q->where('document_lists.id', $data['documentListId']);
                });
            }

            $estimatedCount = $userQuery->count();

            Log::info('User export started (API)', [
                'committee_id' => $data['committeeId'] ?? null,
                'workgroup_id' => $data['workGroupId'] ?? null,
                'document_list_id' => $data['documentListId'] ?? null,
                'estimated_records' => $estimatedCount,
                'export_name' => $name . $ext,
                'sanitized_filename' => true,
                'memory_limit' => ini_get('memory_limit'),
                'time_limit' => ini_get('max_execution_time')
            ]);

            $startTime = microtime(true);
            $startMemory = memory_get_usage(true);

            $response = Excel::download(new UsersExport($data), $name . $ext);

            $endTime = microtime(true);
            $endMemory = memory_get_usage(true);
            $peakMemory = memory_get_peak_usage(true);

            Log::info('User export completed successfully (API)', [
                'execution_time' => round($endTime - $startTime, 2) . 's',
                'memory_used' => round(($endMemory - $startMemory) / 1024 / 1024, 2) . 'MB',
                'peak_memory' => round($peakMemory / 1024 / 1024, 2) . 'MB',
                'estimated_records' => $estimatedCount,
                'export_name' => $name . $ext
            ]);

            return $response;

        } catch (\Exception $e) {
            Log::error('User export failed (API)', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'memory_usage' => round(memory_get_usage(true) / 1024 / 1024, 2) . 'MB',
                'peak_memory' => round(memory_get_peak_usage(true) / 1024 / 1024, 2) . 'MB',
                'export_name' => $name . $ext,
                'committee_id' => $data['committeeId'] ?? null,
                'workgroup_id' => $data['workGroupId'] ?? null,
                'document_list_id' => $data['documentListId'] ?? null
            ]);

            return response()->json([
                'error' => 'Export failed. Please try again or contact support.',
                'message' => config('app.debug') ? $e->getMessage() : 'Internal server error'
            ], 500);
        } finally {
            // Clean up memory
            if (function_exists('gc_collect_cycles')) {
                gc_collect_cycles();
            }
        }
    }

    public function postWorkGroups(UserFrontendWorkGroup $request, UserFrontendService $userFrontendService)
    {
        $userData = Auth::user();

        if(empty($userData)) {
            abort(404);
        }

        $data = $request->validated();

        $workGroups = $request->input('wg', []);
        $data['workGroups'] = $workGroups;

        $typesWorkGroups = $request->input('wg_type', []);
        $data['typesWorkGroups'] = $typesWorkGroups;

        if(count($workGroups) !== count($typesWorkGroups)) {
            return response()->json(null, 404);
        }

        $committees = $request->input('committees', []);
        $data['committees'] = $committees;

        $typesCommittees = $request->input('committees_type', []);
        $data['typesCommittees'] = $typesCommittees;

        if(count($committees) !== count($typesCommittees)) {
            return response()->json(null, 404);
        }

        $user = $userFrontendService->editWGCommitteesUser($userData->id, $data);

        if ($user) {
            return new UserResource($user);
        }
    }

    public function postForgotPassword(UserFrontendEmailCheck $request)
    {
        $data = $request->validated();

        Password::sendResetLink($data, function($user, $token) {
            $user->sendPasswordUserResetNotification($token, $user->email);
        });

        return response(['message' => 'Success']);
    }

    public function postResetPassword(UserFrontendResetPassword $request)
    {
        $data = $request->validated();

        $status = Password::reset(
            $data,
            function (User $user, string $password) {
                $user->forceFill([
                    'password' => Hash::make($password)
                ])->setRememberToken(Str::random(90));
                $user->save();

                event(new PasswordReset($user));
            }
        );

        switch ($status) {
            case Password::PASSWORD_RESET:
                return response()->json(['message' => 'Password reset successfully.'], 200);
            case Password::INVALID_TOKEN:
                return response()->json(['message' => 'The password reset token has expired. Please request a new password reset.'], 400);
            case Password::INVALID_USER:
                return response()->json(['message' => 'No user could be found with this email address.'], 404);
            case Password::RESET_THROTTLED:
                return response()->json(['message' => 'Too many password reset attempts. Please try again later.'], 429);
            default:
                return response()->json(['message' => 'Password reset failed.'], 400);
        }
    }

    public function getUserWorkGroups()
    {
        $userId = Auth::id();
        $userData = User::where('id', $userId)
            ->with(['workGroups' => function ($query) {
                $query->full()->where('status', 'published');
            }])
            ->first();
        $workGroups = $userData->workGroups;

        if ($workGroups) {
            return WorkGroupSimpleResource::collection($workGroups);
        }

        return response()->json(null, 404);
    }

    public function getUserCommitteesWorkGroups()
    {
        $userId = Auth::id();
        $userData = User::where('id', $userId)
            ->with([
                'committees' => function ($query) use ($userId) {
                    $query
                        ->full()
                        ->where('status', 'published')
                        ->with(['workGroups' => function ($query) use ($userId) {
                            $query->full()
                                ->where('status', 'published')
                                ->join('user_work_group', 'work_groups.id', 'user_work_group.work_group_id')
                                ->where('user_work_group.user_id', $userId);
                        }]);
                }])
            ->first();

        $committees = $userData->committees;

        if ($committees) {
            return CommitteeWithWorkGroupsListResource::collection($committees);
        }

        return response()->json(null, 404);
    }

    public function postUserJoinDocumentList(UserJoinDocumentList $request)
    {
        $userId = Auth::id();
        $user = User::find($userId);

        $data = $request->validated();

        if (!empty($data['documentListId'])) {
            $documentList = DocumentList::find($data['documentListId']);

            if ($documentList) {

                $committeeId = $data['committeeId'] ?? null;
                $workGroupId = $data['workGroupId'] ?? null;

                $isUserInCommittee = $committeeId ? $user->committees()->where('id', $committeeId)->exists() : false;
                $isUserInWorkGroup = $workGroupId ? $user->workGroups()->where('id', $workGroupId)->exists() : false;

                if ($isUserInCommittee || $isUserInWorkGroup) {

                    if ($data['join']) {
                        $user->documentLists()->syncWithoutDetaching([$documentList->id]);
                    }
                    else {
                        $user->documentLists()->detach($documentList->id);
                    }

                    return response()->json([
                        'message' => $data['join'] ? 'User joined the Document List successfully' : 'User left the Document List successfully'
                    ], 200);

                } else {
                    return response()->json(['message' => 'User does not belong to the committee or workgroup'], 403);
                }
            } else {
                return response()->json(['message' => 'DocumentList not found'], 404);
            }
        }

        return response()->json(['message' => 'Invalid request'], 400);
    }

    public function getUserCheckDocumentList(UserCheckDocumentList $request)
    {
        $userId = Auth::id();
        $user = User::find($userId);

        $data = $request->validated();

        if (!empty($data['documentListId'])) {
            $documentList = DocumentList::find($data['documentListId']);

            if ($documentList) {

                $committeeId = $data['committeeId'] ?? null;
                $workGroupId = $data['workGroupId'] ?? null;

                $isUserInCommittee = $committeeId ? $user->committees()->where('id', $committeeId)->exists() : false;
                $isUserInWorkGroup = $workGroupId ? $user->workGroups()->where('id', $workGroupId)->exists() : false;

                if ($isUserInCommittee || $isUserInWorkGroup) {

                    $isUserInDocumentList = $user->documentLists()->where('document_list_id', $documentList->id)->exists();

                    return response()->json([
                        'isUserInDocumentList' => $isUserInDocumentList,
                        'message' => $isUserInDocumentList ? 'User is in the Document List' : 'User is not in the Document List'
                    ], 200);

                } else {
                    return response()->json(['message' => 'User does not belong to the committee or workgroup'], 403);
                }
            } else {
                return response()->json(['message' => 'DocumentList not found'], 404);
            }
        }

        return response()->json(['message' => 'Invalid request'], 400);
    }

    protected function verify(EmailVerification $request)
    {
        $request->fulfill();

        return response(['message' => __('response.email_verified')]);
    }

    protected function resend(UserIdShow $request, UserFrontendService $userFrontendService)
    {
        $data = $request->validated();
        $userId = $data['id'];
        $user = $userFrontendService->getUser($userId);
        if (empty($user)) {
            return response(['message' => 'User does not exist.']);
        }
        if ($user->state !== 'pendingEmailValidation') {
            return response(['message' => 'This email is already validated.']);
        }
        event(new Registered($user));
        return response(['message' => 'Resend email verification.']);
    }

    /**
     * GDPR Self-deletion: Allow users to delete their own account
     * This implements the "Right to be Forgotten" under EU GDPR Article 17
     *
     * @param UserDelete $request
     * @param UserFrontendService $userFrontendService
     * @return \Illuminate\Http\JsonResponse
     */
    public function deleteUser(UserDelete $request, UserFrontendService $userFrontendService) {
        $data = $request->validated();
        $response = $userFrontendService->deleteUser($data['id']);

        if ($response['success']) {
            return new AccountDeletionResource($response);
        } else {
            return response()->json([
                'message' => $response['message']
            ], 400);
        }
    }
}
