<?php

namespace App\Services\Hr;

use App\Enums\Role;
use App\Models\Employee;
use App\Models\User;
use App\Models\UserProfile;
use App\Services\Media\MediaService;
use App\Support\Media\MediaPresenter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;

class EmployeeService
{
    public function __construct(
        private readonly MediaService $mediaService,
    ) {}

    public function paginateForIndex(
        array $tableQuery,
        string $role = '',
        string $gender = '',
        string $employmentStatus = '',
        string $isActive = '',
    ): LengthAwarePaginator {
        $query = User::query()
            ->with(['profile', 'employee', 'roles'])
            ->whereDoesntHave('roles', fn ($query) => $query->where('name', Role::DEVELOPER->value))
            ->when($tableQuery['search'] !== '', function ($query) use ($tableQuery): void {
                $search = $tableQuery['search'];
                $query->where(function ($query) use ($search): void {
                    $query->where('email', 'like', "%{$search}%")
                        ->orWhere('username', 'like', "%{$search}%")
                        ->orWhereHas('profile', function ($query) use ($search): void {
                            $query->where('full_name', 'like', "%{$search}%")
                                ->orWhere('phone_number', 'like', "%{$search}%");
                        });
                });
            })
            ->when($role !== '', fn ($query) => $query->whereHas('roles', fn ($query) => $query->where('name', $role)))
            ->when($gender !== '', fn ($query) => $query->whereHas('profile', fn ($query) => $query->where('gender', $gender)))
            ->when($employmentStatus !== '', function ($query) use ($employmentStatus): void {
                $query->whereHas('employee', fn ($query) => $query->where('employment_status', $employmentStatus));
            })
            ->when($isActive !== '', fn ($query) => $query->where('is_active', $isActive === '1'));

        $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);

        return $query
            ->paginate(25)
            ->withQueryString();
    }

    public function assignableRoleOptions(): array
    {
        return Role::assignableSelectOptions();
    }

    public function findForEdit(User $user): array
    {
        $user->load(['profile', 'employee', 'roles']);

        return [
            'employee' => $user,
            'profilePhoto' => $user->profile
                ? MediaPresenter::first($user->profile, 'profile_photo')
                : null,
        ];
    }

    public function create(array $validated): void
    {
        try {
            DB::transaction(function () use ($validated): void {
                $user = User::create([
                    'email' => $validated['email'],
                    'username' => $validated['username'],
                    'password' => Hash::make(config('auth.password_default')),
                ]);

                $profile = UserProfile::create([
                    'user_id' => $user->id,
                    'full_name' => $validated['full_name'],
                    'phone_number' => $validated['phone_number'],
                    'gender' => $validated['gender'],
                    'birth_date' => $validated['birth_date'],
                    'address' => $validated['address'],
                ]);

                $this->syncProfilePhoto($profile, $validated);

                if ($validated['role'] !== Role::OWNER->value) {
                    Employee::create([
                        'user_id' => $user->id,
                        'join_date' => $validated['join_date'],
                        'employment_status' => $validated['employment_status'],
                        'base_salary' => $validated['base_salary'],
                    ]);
                }

                $user->syncRoles([$validated['role']]);
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal membuat karyawan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }
    }

    public function update(User $user, array $validated): void
    {
        $employee = $user->employee;

        try {
            DB::transaction(function () use ($validated, $user, $employee): void {
                $user->update([
                    'email' => $validated['email'],
                    'username' => $validated['username'],
                ]);

                $profile = $user->profile()->updateOrCreate(
                    ['user_id' => $user->id],
                    [
                        'full_name' => $validated['full_name'],
                        'phone_number' => $validated['phone_number'],
                        'gender' => $validated['gender'],
                        'birth_date' => $validated['birth_date'],
                        'address' => $validated['address'],
                    ],
                );

                $this->syncProfilePhoto($profile, $validated);

                $hasEmployee = ! empty($validated['join_date']) && ! empty($validated['employment_status']) && ! empty($validated['base_salary']);

                if ($hasEmployee) {
                    if ($employee) {
                        $employee->update([
                            'join_date' => $validated['join_date'],
                            'employment_status' => $validated['employment_status'],
                            'base_salary' => $validated['base_salary'],
                        ]);
                    } else {
                        Employee::create([
                            'user_id' => $user->id,
                            'join_date' => $validated['join_date'],
                            'employment_status' => $validated['employment_status'],
                            'base_salary' => $validated['base_salary'],
                        ]);
                    }
                } else {
                    if ($employee) {
                        $employee->delete();
                    }
                }

                $user->syncRoles([$validated['role']]);
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal memperbarui karyawan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }
    }

    public function toggleStatus(User $user, array $validated): void
    {
        $user->update([
            'is_active' => $validated['is_active'],
        ]);

        if (! $validated['is_active']) {
            DB::table('sessions')->where('user_id', $user->id)->delete();
        }
    }

    public function resetPassword(User $user): void
    {
        $user->update([
            'password' => config('auth.password_default'),
        ]);

        DB::table('sessions')->where('user_id', $user->id)->delete();
    }

    public function delete(User $user): void
    {
        try {
            DB::transaction(function () use ($user): void {
                $user->employee?->delete();
                $user->profile?->delete();
                $user->delete();
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal menghapus karyawan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }
    }

    private function syncProfilePhoto(UserProfile $profile, array $validated): void
    {
        $newFiles = ! empty($validated['profile_photo']) ? [$validated['profile_photo']] : [];
        $removeIds = $validated['remove_profile_photo_ids'] ?? [];

        $this->mediaService->syncCollection($profile, 'profile_photo', $newFiles, $removeIds, 1);
    }

    private function applySorting(Builder $query, string $sort, string $direction): void
    {
        $employeeSorts = [
            'join_date',
            'base_salary',
            'employment_status',
        ];

        if (in_array($sort, $employeeSorts, true)) {
            $query->orderBy(
                Employee::select($sort)
                    ->whereColumn('employees.user_id', 'users.id')
                    ->limit(1),
                $direction
            );

            return;
        }

        if ($sort === 'full_name') {
            $query->orderBy(
                UserProfile::select('full_name')
                    ->whereColumn('user_profiles.user_id', 'users.id')
                    ->limit(1),
                $direction
            );

            return;
        }

        if ($sort === 'email') {
            $query->orderBy('email', $direction);

            return;
        }

        $query->latest();
    }
}
