<?php

namespace App\Libs\Services;

use App\Models\Committee;
use App\Models\Member;
use App\Models\User;
use App\Notifications\ApproveUser;
use App\Notifications\InactiveUser;
use App\Services\MicrosoftGraph\UserMembershipService;
use Illuminate\Support\Facades\DB;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Image;

class UserFrontendService
{
    protected $userMembershipService;

    public function __construct(UserMembershipService $userMembershipService)
    {
        $this->userMembershipService = $userMembershipService;
    }

    /**
     * Get the user list for the backoffice grid
     *
     * @param $page
     * @param $rowCount
     * @param $sort
     * @param $search
     *
     * @return array
     */
    public function getUserGridList($page, $rowCount, $sort, $search, $status, $wg, $committee, $memberId)
    {
        Paginator::currentPageResolver( function () use ($page) {
            return $page;
        });

        if (!empty($search)) {
            $query = User::where(function ($query) use ($search) {
                $query->orWhere('name', 'like', "%{$search}%")
                    ->orWhere('last_name', 'like', "%{$search}%")
                    ->orWhere('state', 'like', "%{$search}%")
                    ->orWhere('department', 'like', "%{$search}%")
                    ->orWhere('function', 'like', "%{$search}%");
            });
        } else {
            $query = User::query();
        }

        if (!empty($status)) {
            $query->where('state', $status);
        }

        if (!empty($wg) && $wg > 0) {
            $query->whereExists(function ($query) use ($wg) {
                $query->select('work_group_id')
                    ->from('user_work_group')
                    ->where('work_group_id', $wg)
                    ->whereRaw('user_work_group.user_id = users.id');
            });
        }

        if (!empty($committee) && $committee > 0) {
            $query->whereExists(function ($query) use ($committee) {
                $query->select('committee_id')
                    ->from('committee_user')
                    ->where('committee_id', $committee)
                    ->whereRaw('committee_user.user_id = users.id');
            });
        }

        if (!empty($memberId)) {
            $query->where('member_id', $memberId);
        }

        if (empty($sort)) {
            $rows = $query->paginate($rowCount);
        } else {
            foreach ($sort as $field => $direction) {
                if(empty($direction) || !Str::is(strtolower($direction),"desc")){
                    $orderDirection = "asc";
                } else {
                    $orderDirection = $direction;
                }
                $rows = $query->orderBy($field, strtolower($orderDirection))->paginate($rowCount);
                break;
            }
        }

        return [
            "total"             => $rows->total(),
            "current"           => $rows->currentPage(),
            "rowCount"          => $rowCount,
            "rows"              => $rows->getCollection()
        ];
    }

    public function createUser($data)
    {
        $user = new User();

        $user->name = $data['name'];
        $user->last_name = $data['lastName'];
        $user->email = $data['email'];
        $user->password = Hash::make($data['password']);
        $user->state = $data['state'];

        if (!empty($data['department'])) {
            $user->department = $data['department'];
        }

        if (!empty($data['function'])) {
            $user->function = $data['function'];
        }

        if (!empty($data['phoneNumber'])) {
            $user->phone_number = $data['phoneNumber'];
        }

        $user->accepted_terms = $data['acceptedTerms'];
        $user->member_id = $data['memberId'];

        $member = Member::find($data['memberId']);
        $user->profile_status = $member->member_type === 'noType' ? "completed" : "step_1";
        $user->save();

        return $user;
    }

    public function editUser($userId, $data)
    {
        $user = User::findOrFail($userId);

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

        return $this->_fillUserData($user, $data);
    }

    protected function _fillUserData(User $user, $data)
    {
        $user->name = $data["name"];
        $user->last_name = $data["lastName"];

        if (!empty($data['department'])) {
            $user->department = $data['department'];
        }

        if (!empty($data['function'])) {
            $user->function = $data['function'];
        }

        if (!empty($data['phoneNumber'])) {
            $user->phone_number = $data['phoneNumber'];
        }

        if (!empty($data['password'])) {
            $user->password = Hash::make($data['password']);
        }

        if (!empty($data['state'])) {
            if ($data['state'] === 'active' && $user->state === 'pendingApproval') {
                $user->notify(new ApproveUser());
            } else if ($data['state'] === 'inactive' && $user->state !== 'inactive') {
                $user->notify(new InactiveUser());
            }
            $user->state = $data['state'];
        }

        $user->legal_opinion_permission = !empty($data["legal_opinion_permission"]) ? 1 : 0;

        $previousFinancePermission = $user->finance_sub_committee_permission;
        $newFinancePermission = !empty($data["finance_sub_committee_permission"]) ? 1 : 0;
        $user->finance_sub_committee_permission = $newFinancePermission;

        DB::transaction(function () use ($user, $data, $previousFinancePermission, $newFinancePermission) {
            $committees = [];
            foreach ($data['committees'] as $index => $committeesId) {
                if (isset($data['typesCommittees'][$index])) {
                    $committees[$committeesId] = ['type' => $data['typesCommittees'][$index]];
                }
            }

            // Handle Finance Sub-Committee permission
            $financeSubCommittee = Committee::where('type', 'financeSubCommittee')->first();
            if ($financeSubCommittee) {
                if ($newFinancePermission && !$previousFinancePermission) {
                    // Permission was activated - add user to finance sub-committee
                    if (!array_key_exists($financeSubCommittee->id, $committees)) {
                        $committees[$financeSubCommittee->id] = ['type' => 'member'];
                    }
                } else if (!$newFinancePermission && $previousFinancePermission) {
                    // Permission was deactivated - remove user from finance sub-committee
                    unset($committees[$financeSubCommittee->id]);
                } else if ($newFinancePermission) {
                    // Permission is still active - ensure user remains in committee
                    if (!array_key_exists($financeSubCommittee->id, $committees)) {
                        $committees[$financeSubCommittee->id] = ['type' => 'member'];
                    }
                }
            }

            $legalCommittee = Committee::where('type', 'legalCommittee')->first();
            $legalOpinion = Committee::where('type', 'legalOpinions')->first();

            if ($legalCommittee && $legalOpinion) {
                $isInLegalCommittee = DB::table('committee_member')
                    ->where('committee_id', $legalCommittee->id)
                    ->where('member_id', $user->member_id)
                    ->exists();

                $isInLegalOpinion = DB::table('committee_member')
                    ->where('committee_id', $legalOpinion->id)
                    ->where('member_id', $user->member_id)
                    ->exists();

                if (
                    $isInLegalCommittee &&
                    $isInLegalOpinion &&
                    array_key_exists($legalCommittee->id, $committees) &&
                    !array_key_exists($legalOpinion->id, $committees)
                ) {
                    $committees[$legalOpinion->id] = ['type' => 'to'];
                }
            }

            $this->userMembershipService->syncUserCommittees($user, $committees);

            $workGroups = [];
            foreach ($data['workGroups'] as $index => $workGroupId) {
                if (isset($data['typesWorkGroups'][$index])) {
                    $workGroups[$workGroupId] = ['type' => $data['typesWorkGroups'][$index]];
                }
            }
            $this->userMembershipService->syncUserWorkGroups($user, $workGroups);

            $user->save();
        });

        return $user;
    }

    public function list($data)
    {
        // $userId = Auth::id();
        $query = User::select('users.*')
            ->where('users.state', 'active');

        $orderDir = $data['orderDir'] ? $data['orderDir'] : 'asc';

        if (!empty($data['committeeId'])) {
            /*
            $workGroupIds = WorkGroup::where('committee_id', $data['committeeId'])
                ->pluck('id');

            $query->whereHas('workGroups', function ($q) use ($workGroupIds) {
                $q->whereIn('work_group_id', $workGroupIds);
            });
            */
            $query->whereHas('committees', function ($query) use ($data) {
                $query->where('committee_id', $data['committeeId']);
            });
        } else if (!empty($data['workGroupId'])) {
            $query->whereHas('workGroups', function ($q) use ($data) {
                $q->where('work_group_id', $data['workGroupId']);
            });
        } else if (!empty($data['documentListId'])) {
            $query->whereHas('documentLists', function ($q) use ($data) {
                $q->where('document_list_id', $data['documentListId']);
            });
        }

        if (!empty($data['memberId'])) {
            $query->where('member_id', $data['memberId']);
        }

        switch ($data['orderBy']) {
            case 'company':
                $query->join('members', 'member_id', '=', 'members.id')
                    ->orderBy('members.name', $orderDir);
                break;
            default:
                # name
                $query->orderBy('users.name', $orderDir)
                    ->orderBy('users.last_name', $orderDir);
                break;
        }

        $perPage = $data['perPage'] ?? 12;
        $page = $data['page'] ?? 1;

        if (!empty($data['showCommitteesWorkgroups'])) {
            $query->with([
                'member',
                'committees' => function ($query) {
                    $query->full()
                        ->where('status', 'published')
                        ->with(['workGroups' => function ($query)  {
                            $query->full()->where('status', 'published');
                        }]);
                }]
            );
        } else {
            $query->with(['member']);
        }

        $users = $query->paginate($perPage, ['*'], 'page', $page);

        $paginationData = [
            'currentPage' =>  intval($users->currentPage()),
            'lastPage' => intval($users->lastPage()),
            'perPage' =>  intval($users->perPage()),
            'total' =>  intval($users->total()),
            'count' =>  intval($users->count()),
            'hasMorePages' => $users->hasMorePages(),
            'nextPageUrl' => $users->nextPageUrl(),
            'prevPageUrl' => $users->previousPageUrl(),
        ];

        return [
            'users' => $users,
            'pagination' => $paginationData,
        ];
    }

    public function updateUser($data)
    {
        $user = User::find($data['id']);

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

        $user->name = $data['name'];
        $user->last_name = $data['lastName'];

        if (!empty($data['department'])) {
            $user->department = $data['department'];
        }

        if (!empty($data['function'])) {
            $user->function = $data['function'];
        }

        if (!empty($data['phoneNumber'])) {
            $user->phone_number = $data['phoneNumber'];
        }

        $user->save();

        return $user;
    }

    public function getUser($userId)
    {
        $user = User::find($userId);
        return $user;
    }

    public function editWGCommitteesUser ($userId, $data)
    {
        $user = User::findOrFail($userId);

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

        $workGroups = [];
        $workGroupsIds = [];
        foreach ($data['workGroups'] as $index => $workGroupId) {
            if (isset($data['typesWorkGroups'][$index])) {
                $workGroups[$workGroupId] = ['type' => $data['typesWorkGroups'][$index]];
                array_push($workGroupsIds, $workGroupId);
            }
        }

        $this->userMembershipService->syncUserWorkGroups($user, $workGroups);

        $committees = [];
        $committeesIds = [];
        foreach ($data['committees'] as $index => $committeeId) {
            if (isset($data['typesCommittees'][$index])) {
                $committees[$committeeId] = ['type' => $data['typesCommittees'][$index]];
                array_push($committeesIds, $committeeId);
            }
        }

        $legalCommittee = Committee::where('type', 'legalCommittee')->first();
        $legalOpinion = Committee::where('type', 'legalOpinions')->first();

        if ($legalCommittee && $legalOpinion) {
            $isInLegalCommittee = DB::table('committee_member')
                ->where('committee_id', $legalCommittee->id)
                ->where('member_id', $user->member_id)
                ->exists();

            $isInLegalOpinion = DB::table('committee_member')
                ->where('committee_id', $legalOpinion->id)
                ->where('member_id', $user->member_id)
                ->exists();

            if (
                $isInLegalCommittee &&
                $isInLegalOpinion &&
                in_array($legalCommittee->id, $committeesIds)
            ) {
                if ($data["addLegalOpinion"] && !in_array($legalOpinion->id, $committeesIds)) {
                    $committees[$legalOpinion->id] = ['type' => 'to'];
                    $committeesIds[] = $legalOpinion->id;
                }
            } else {
                if (!in_array($legalOpinion->id, $committeesIds)) {
                    $this->userMembershipService->detachUserFromCommittee($user, $legalOpinion->id);
                }
            }
        }

        /* $committeesExtrasIds = WorkGroup::whereIn('id', $workGroupsIds)
            ->whereNotIn('committee_id', $committeesIds)
            ->pluck('committee_id')
            ->unique();

        foreach ($committeesExtrasIds as $index => $committeeId) {
            $committees[$committeeId] = ['type' => 'to'];
        } */

        $this->userMembershipService->syncUserCommittees($user, $committees);

        $user->profile_status = "completed";
        $user->save();

        return $user;
    }

    public function storeProfileImage ($data, $userData)
    {
        $uniqueName = md5(pathinfo($data['file']->getClientOriginalName(), PATHINFO_FILENAME) . microtime(true));
        $fileName = $uniqueName . '.' . $data['file']->getClientOriginalExtension();
        $data['file']->storeAs('profile_images', $fileName, 'public');

        $fileNameThumb = $uniqueName . '_100x100' . '.' . $data['file']->getClientOriginalExtension();
        $targetFilePath = config("path.internal.profile_image")."/{$fileNameThumb}";
        $originalFilePath = config("path.internal.profile_image")."/{$fileName}";
        $img = Image::make(Storage::path($originalFilePath));
        $width = 100;
        $height = 100;
        $img->fit($width, $height);
        $img->save(Storage::path($targetFilePath), 90);

        $user = User::find($userData->id);
        $user->image = $fileName;
        $user->save();

        return $fileName;
    }

    public function changePassword ($data, $userData)
    {
        if (!Hash::check($data['oldPassword'], $userData->password)) {
            return [
                'success' => false,
                'message' => 'The previous password is incorrect.',
                'error_type' => 'invalid_old_password'
            ];
        }

        $userData->password = Hash::make($data['password']);
        $userData->save();

        return [
            'success' => true,
            'message' => 'Password updated successfully.'
        ];
    }

    /**
     * Delete user account (GDPR Right to be Forgotten)
     * Allows users to exercise their right to data deletion under EU GDPR Article 17
     */
    public function deleteUser($userId)
    {
        $user = User::find($userId);

        if (!$user) {
            return [
                'success' => false,
                'message' => 'User account not found',
            ];
        }

        try {
            // Log the GDPR deletion request for audit and compliance
            Log::info('GDPR user self-deletion initiated', [
                'user_id' => $user->id,
                'user_email' => $user->email,
                'user_name' => $user->name . ' ' . ($user->last_name ?? ''),
                'member_id' => $user->member_id,
                'initiated_by_user_id' => auth()->id(),
                'gdpr_article' => 'Article 17 - Right to erasure',
                'deletion_timestamp' => now()->toISOString(),
                'ip_address' => request()->ip(),
                'user_agent' => request()->userAgent()
            ]);

            // IMPORTANT: Revoke all tokens BEFORE deletion for GDPR compliance
            // This ensures the user is immediately logged out after account deletion
            try {
                if (method_exists($user, 'tokens')) {
                    $user->tokens()->delete(); // Revoke all Sanctum tokens
                }

                // Also logout current session if it's the same user
                if (auth()->check() && auth()->id() === $user->id) {
                    auth()->guard('sanctum')->logout();
                }
            } catch (\Exception $e) {
                // Log token revocation error but don't fail the deletion
                Log::warning('Failed to revoke user tokens during GDPR deletion', [
                    'user_id' => $user->id,
                    'error' => $e->getMessage()
                ]);
            }

            // Delete the user (this will trigger the Model's deleting observer)
            $user->delete();

            // Log successful deletion for compliance records
            Log::info('GDPR user self-deletion completed successfully', [
                'user_id' => $userId,
                'user_email' => $user->email,
                'completed_at' => now()->toISOString()
            ]);

            return [
                'success' => true,
                'message' => 'Your account and personal data have been permanently deleted.',
            ];

        } catch (\Exception $e) {
            Log::error('GDPR user self-deletion failed', [
                'user_id' => $userId,
                'user_email' => $user->email ?? 'unknown',
                'error' => $e->getMessage(),
                'stack_trace' => $e->getTraceAsString(),
                'failed_at' => now()->toISOString()
            ]);

            return [
                'success' => false,
                'message' => 'Failed to delete your account. Please contact support for assistance.',
            ];
        }
    }
}
