<?php

namespace App\Services;

use Exception;
use Illuminate\Http\Request;
use App\Services\EmailService;
use App\Libs\Enum\UserTypeEnum;
use App\Services\UploadService;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
use App\Models\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use App\Repositories\Interfaces\ProfileRepositoryInterface;
use App\Repositories\Interfaces\VerificationCodeRepositoryInterface;

class UserService
{
    /**
     * @var UserRepositoryInterface
     */
    protected $userRepository;

    /**
     * @var ProfileRepositoryInterface
     */
    protected $profileRepository;

    /**
     * @var VerificationCodeRepositoryInterface
     */
    protected $verificationCodeRepository;

    /**
     * @var EmailService
     */
    protected $emailService;

    /**
     * @var UploadService
     */
    protected $uploadService;

    private $response = [
        'success' => true,
        'message' => null
    ];

    public function __construct(
        UserRepositoryInterface $userRepository, ProfileRepositoryInterface $profileRepository,
        VerificationCodeRepositoryInterface $verificationCodeRepository,
        EmailService $emailService,
        UploadService $uploadService
    )
    {
        $this->userRepository = $userRepository;
        $this->profileRepository = $profileRepository;
        $this->verificationCodeRepository = $verificationCodeRepository;
        $this->emailService = $emailService;
        $this->uploadService = $uploadService;
    }

    public function all()
    {
        try {
            $users = $this->userRepository->all();
            $this->response['message'] = "Data retrieved";
            $this->response['data'] = $users;
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            
            $this->response['success'] = false;
            $this->response['message'] = "Error retrieving data";
            return $this->response;
        }
    }

    /**
     * @param array $payload
     *
     * @return array
     */
    public function store(array $payload): array
    {
        try {
            $this->userRepository->create($payload);
            $this->response['message'] = "Data created";
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Bad Request";
            return $this->response;
        }
    }

    /**
     * @param int $id
     *
     * @return array
     */
    public function get(int $id): array
    {
        try {
            $user = $this->userRepository->find($id);
            $this->response['message'] = "Data retrieved";
            $this->response['data'] = $user;
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Error retrieving data";
            return $this->response;
        }
    }

    /**
     * @param array $payload
     * @param int $id
     *
     * @return array
     */
    public function update(array $payload, int $id): array
    {
        try {
            $user = $this->userRepository->fill($payload, $id);
            if ($user->isDirty()) {
                $this->userRepository->update($payload, $id);
                $this->response['message'] = "Data updated";
            } else {
                $this->response['message'] = "No changes detected";
            }
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Bad Request";
            return $this->response;
        }

    }

    /**
     * @param int $id
     *
     * @return array
     */
    public function delete(int $id): array
    {
        try {
            $this->userRepository->delete($id);
            $this->response['message'] = "Data deleted";
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Bad Request";
            return $this->response;
        }
    }

    /**
     * @param string $userUuid
     * 
     * @return array
     */
    public function getProfile(string $userUuid): array
    {
        try {
            $user = $this->userRepository->getByColumn('uuid', $userUuid);
            $userProfile = $user->profile->toArray();
            
            unset($userProfile['id']);
            unset($userProfile['user_id']);
            unset($userProfile['created_at']);
            unset($userProfile['updated_at']);

            $data = [
                'email' => $user->email,
                ...$userProfile
            ];

            $data['image'] = $data['image'] ? 
                $this->uploadService->generatePublicLink('users', ['id' => $user->id, 'column' => $data['image']]) :
                null;

            if ($user->role_id === UserTypeEnum::PROFESSIONAL->value) {
                
                $professional = $user->professional;
                if (!$professional) {
                    throw new Exception('Professional profile not found');
                }

                $professionalInfo = $professional->toArray();
                
                unset($professionalInfo['id']);
                unset($professionalInfo['user_id']);
                unset($professionalInfo['created_at']);
                unset($professionalInfo['updated_at']);
                unset($professionalInfo['has_applied']);

                $professionalInfo['salutation'] = $professional->profession?->salutation;
                $professionalInfo['profession_name'] = $professional->profession?->name;
                $professionalInfo['profession_color_hex'] = $professional->profession?->color_hex;
                $professionalInfo['ratings_count'] = $professional->ratings->count();
                $professionalInfo['rate_average'] = round((float) ($professional->ratings->avg('rate') ?? 0), 1);

                // Specialties
                $specialties = $professional->specialties;
                $professionalInfo['specialties'] = [];
                foreach ($specialties as $specialtyProfession) {
                    $specialtyData = [
                        'specialty_id' => $specialtyProfession->specialty_id,
                        'name' => $specialtyProfession->custom_name ?: $specialtyProfession->specialty?->name
                    ];
                    $professionalInfo['specialties'][] = $specialtyData;
                }
                
                // Office
                $office = $professional->office?->toArray() ?? [];

                unset($office['id'], $office['professional_id'], $office['user_id']);
                unset($office['created_at'], $office['updated_at']);

                $professionalInfo['office'] = $office;

                // Schedules
                $schedules = $professional->schedules->toArray();
                $professionalInfo['schedules'] = [];

                foreach ($schedules as $schedule) {
                    $scheduleData = [
                        'day_of_week' => $schedule['day_of_week'],
                        'time_from' => $schedule['time_from'],
                        'time_to' => $schedule['time_to'],
                        'rate' => $schedule['rate']
                    ];
                    $professionalInfo['schedules'][] = $scheduleData;
                }

                $data['professional_info'] = $professionalInfo;
            }   
            $this->response['success'] = true;
            $this->response['message'] = "Data retrieved";
            $this->response['data'] = $data;
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Bad Request";
            return $this->response;
        }
    }
    
    /**
     * @param string $uuid
     * @param Request $request
     * 
     * @return array
     */
    public function updateProfile(string $uuid, Request $request): array
    {
        try {
            $user = $this->userRepository->getByColumn('uuid', $uuid);
            $payload = $request->all();
            if (!$user) {
                $this->response['success'] = false;
                $this->response['message'] = "Bad Request: User not found";
                return $this->response;
            }
            if (isset($payload['image'])) {
                $imageFile = $request->file('image');
                $payload['image'] = $this->uploadService->processAttachments($imageFile, 'users/' . $user->id);
            }
            
            $firstName = $payload['first_name'] ?? $user->profile->first_name;
            $lastName = $payload['last_name'] ?? $user->profile->last_name;

            $payload['full_name'] = $firstName.' '.$lastName;

            $this->profileRepository->update($payload, $user->profile->id);
            $this->response['message'] = "Profile Updated";
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Bad Request";
            return $this->response;
        }
    }

    /**
     * @param string $uuid
     * @param array $payload
     * 
     * @return array
     */
    public function changePassword(string $uuid, array $payload): array
    {
        try {
            $currentInputtedPassword = $payload['current_password'];

            $user = $this->userRepository->getByColumn('uuid', $uuid);
            if (!Hash::check($currentInputtedPassword, $user->password)) {
                $this->response['success'] = false;
                $this->response['message'] = "Incorrect Password";
                return $this->response;
            }
            $updatePayload = [
                'password' => Hash::make($payload['password']),
            ];
            $this->userRepository->update($updatePayload, $user->id);
            $this->response['message'] = "Password has been changed successfully";
            return $this->response;
        } catch (Exception $e) {
            app('log')->error("Error", ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = "Bad Request";
            return $this->response;
        }
    }

    public function updatePushToken(string $uuid, string $pushToken, string $platform = 'unknown', ?string $deviceId = null): array
    {
        try {
            $user = $this->userRepository->getByColumn('uuid', $uuid);

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

            DB::transaction(function () use ($pushToken, $platform, $deviceId, $user) {
                $hash = hash('sha256', trim($pushToken));
                $previousUserIds = \App\Models\UserPushToken::where('token_hash', $hash)
                    ->where('user_id', '!=', $user->id)->pluck('user_id');
                if ($deviceId) {
                    \App\Models\UserPushToken::where('user_id', $user->id)
                        ->where('platform', $platform)->where('device_id', $deviceId)
                        ->where('token_hash', '!=', $hash)->delete();
                }
                \App\Models\UserPushToken::updateOrCreate(
                    ['token_hash' => $hash],
                    ['user_id' => $user->id, 'token' => trim($pushToken), 'platform' => $platform, 'device_id' => $deviceId, 'last_seen_at' => now()]
                );
                // Keep the legacy column populated for older server code during rollout.
                $this->userRepository->update(['push_token' => trim($pushToken)], $user->id);
                foreach ($previousUserIds as $previousUserId) {
                    $replacement = \App\Models\UserPushToken::where('user_id', $previousUserId)
                        ->latest('last_seen_at')->value('token');
                    \App\Models\User::whereKey($previousUserId)->update(['push_token' => $replacement]);
                }
            });

            app('log')->info('Push token updated', [
                'user_uuid' => $uuid,
                'token_suffix' => substr($pushToken, -8),
            ]);

            return ['success' => true, 'message' => 'Push token updated'];
        } catch (Exception $e) {
            app('log')->error('Push token update failed', ['errorMessage' => $e->getMessage()]);

            return ['success' => false, 'message' => 'Unable to update push token'];
        }
    }

    public function deletePushToken(string $uuid, ?string $pushToken, string $platform = 'unknown', ?string $deviceId = null): array
    {
        try {
            $user = $this->userRepository->getByColumn('uuid', $uuid);

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

            DB::transaction(function () use ($pushToken, $platform, $deviceId, $user) {
                $query = \App\Models\UserPushToken::where('user_id', $user->id);
                if ($deviceId) {
                    $query->where('platform', $platform)->where('device_id', $deviceId);
                } else {
                    $query->where('token_hash', hash('sha256', trim($pushToken)));
                }
                $deletedTokens = (clone $query)->pluck('token');
                $query->delete();

                if ($deletedTokens->contains($user->push_token)) {
                    $replacement = \App\Models\UserPushToken::where('user_id', $user->id)
                        ->latest('last_seen_at')->value('token');
                    $this->userRepository->update(['push_token' => $replacement], $user->id);
                }
            });

            return ['success' => true, 'message' => 'Push token deleted'];
        } catch (Exception $e) {
            app('log')->error('Push token deletion failed', ['errorMessage' => $e->getMessage()]);
            return ['success' => false, 'message' => 'Unable to delete push token'];
        }
    }
}
