<?php

namespace Modules\Employee\Repositories;

use App\Models\User;
use Modules\Employee\Models\Employee;
use Illuminate\Support\Facades\Hash;
use Modules\Core\Enums\RolesEnum;
use Modules\Core\Enums\StatusEnum;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Core\Services\ImageUpload;
use Modules\Employee\DTO\EmployeeListDTO;
use Modules\Employee\DTO\EmployeeListFilter;
use Modules\Employee\Filters\EmployeeFilter;
use Modules\Employee\Models\EmpTermination;
use Modules\HR\Models\EmployeeTaxProfile;

class EmployeeRepository
{
    protected $imageUpload;

    public function __construct(
        ImageUpload $imageUpload
    ) {
        $this->imageUpload = $imageUpload;
    }

    public function getEmployeesList(EmployeeListDTO $filters, array $columns = ['*'])
    {
        $query = Employee::query()->select($columns);

        $filter = new EmployeeFilter($filters);
        $filter->apply($query);

        return $query->paginate($filters->perPage);
    }

    public function getEmployees(EmployeeListFilter $filters, array $columns = ['*'])
    {
        return Employee::query()
            ->select($columns)
            ->filterId($filters->id)
            ->filterDepartment($filters->department_id)
            ->filterDesignation($filters->designation_id)
            ->filterProject($filters->project_id)
            ->filterBranch($filters->branch_id)
            ->filterStatus($filters->status)
            ->search($filters->search)
            ->orderBy('emp_number')
            ->paginate($filters->perPage);
    }

    public function createEmployee(array $data, ?int $userId = null): Employee
    {
        $employee = new Employee(
            array_merge($data, ['user_id' => $userId])
        );

        if (!empty($data['emp_number'])) {
            $employee->emp_number = $data['emp_number'];
            $employee->syncEmployeeCounter($data['emp_number']);
        } else {
            $employee->emp_number = $employee->generateEmployeeNumber();
        }

        $employee->save();

        return $employee;
    }

    public function createUser(array $data): User
    {
        return User::create([
            'name'     => $data['name'],
            'email'    => $data['email'],
            'phone'    => $data['phone'],
            'password' => Hash::make($data['password']),
            'status'   => $data['status'] ?? StatusEnum::INACTIVE
        ]);
    }

    public function updateUserId(User $user, string $roleName): void
    {
        $user->update([
            'userId' => $user->generateUserId($roleName),
            'email_verified_at' => now()
        ]);
    }

    public function find(int $id, array $columns = ['*']): Employee
    {
        return Employee::select($columns)->findOrFail($id);
    }

    public function updateEmployee(Employee $employee, array $data): Employee
    {

        $employee->fill($data);

        if ($employee->isDirty('emp_number')) {
            $employee->syncEmployeeCounter($employee->emp_number);
        }

        $employee->save();

        return $employee;
    }

    public function updateEmpContact(Employee $employee, array $data): Employee
    {
        if (isset($data['address'])) {
            $employee->presentAddress()->updateOrCreate(
                [],
                [
                    'address' => $data['address'] ?? null,
                    'postal_code' => $data['postal_code'] ?? null,
                    'country_id' => $data['country_id'] ?? null,
                ]
            );
        }

        $employee->update([
            'email' => $data['email'] ?? null,
            'phone' => $data['phone'] ?? null,
            'work_phone' => $data['work_phone'] ?? null,
        ]);

        return $employee;
    }

    public function updateEmpEmpTaxProfile(Employee $employee, array $data): Employee
    {
        $employee->taxProfile()->updateOrCreate(
            [],
            [
                'employee_id' => $employee->id,
                'tin_number' => $data['tin_number'] ?? null,
                'tax_zone' => $data['tax_zone'] ?? null,
            ]
        );
        return $employee;
    }

    public function updateEmpEmergencyContact(Employee $employee, array $data): Employee
    {
        if (isset($data['emergencies'])) {

            foreach ($data['emergencies'] as $emergency) {

                $employee->emergencies()->updateOrCreate(
                    ['id' => $emergency['id'] ?? null],
                    [
                        'name' => $emergency['name'] ?? null,
                        'phone' => $emergency['phone'] ?? null,
                        'relation' => $emergency['relation'] ?? null,
                        'address' => $emergency['address'] ?? null,
                    ]
                );
            }
        }
        return $employee;
    }

    public function updateEmpJobDetails(Employee $employee, array $data): Employee
    {
        $employee->update([
            'joined_date' => $data['joined_date'] ?? null,
            'department_id' => $data['department_id'] ?? null,
            'designation_id' => $data['designation_id'] ?? null,
            'project_id' => $data['project_id'] ?? null,
            'branch_id' => $data['branch_id'] ?? null,
            'report_to' => $data['report_to'] ?? null,
            'employment_status_id' => $data['employment_status_id'] ?? null,
        ]);

        return $employee->fresh();
    }

    public function updateEmpSalary(Employee $employee, array $data): Employee
    {
        $employee->bankInfo()->updateOrCreate(
            [],
            [
                'bank_name' => $data['bank_name'] ?? null,
                'account_name' => $data['account_name'] ?? null,
                'account_no' => $data['account_no'] ?? null,
                'branch' => $data['branch'] ?? null,
                'routing_no' => $data['routing_no'] ?? null,
            ]
        );

        return $employee;
    }


    public function updateEmpPayroll(Employee $employee, array $data): Employee
    {
        $employee->update([
            'cpf_active' => $data['cpf_active'] ?? $employee->cpf_active,
            'attendance_required' => $data['attendance_required'] ?? $employee->attendance_required,
            'overtime_allowed' => $data['overtime_allowed'] ?? $employee->overtime_allowed,
            'is_payroll' => $data['is_payroll'] ?? $employee->is_payroll,
            'tax_deduction' => $data['tax_deduction'] ?? $employee->tax_deduction,
            'attendance_policy_id' => $data['attendance_policy_id'] ?? $employee->attendance_policy_id,
            'weekend_policy_id' => $data['weekend_policy_id'] ?? $employee->weekend_policy_id,
            'holiday_allowance_policy_id' => $data['holiday_allowance_policy_id'] ?? $employee->holiday_allowance_policy_id,
            'shift_id' => $data['shift_id'] ?? $employee->shift_id,
        ]);

        return $employee;
    }

    public function updateEmpEducations(Employee $employee, array $data): Employee
    {
        if (isset($data['emp_educations'])) {

            foreach ($data['emp_educations'] as $empEducation) {

                $employee->educations()->updateOrCreate(
                    [
                        'id' => $empEducation['id'] ?? null
                    ],
                    [
                        'education_level_id' => $empEducation['education_level_id'] ?? null,
                        'institute' => $empEducation['institute'] ?? null,
                        'major' => $empEducation['major'] ?? null,
                        'year' => $empEducation['year'] ?? null,
                        'score' => $empEducation['score'] ?? null,
                        'scale' => $empEducation['scale'] ?? null,
                        'start_date' => $empEducation['start_date'] ?? null,
                        'end_date' => $empEducation['end_date'] ?? null,
                    ]
                );
            }
        }
        return $employee;
    }


    public function updateEmpExperiences(Employee $employee, array $data): Employee
    {
        if (isset($data['emp_experiences'])) {

            foreach ($data['emp_experiences'] as $empExperience) {

                $employee->experiences()->updateOrCreate(
                    [
                        'id' => $empExperience['id'] ?? null
                    ],
                    [
                        'company' => $empExperience['company'] ?? null,
                        'job_title' => $empExperience['job_title'] ?? null,
                        'from_date' => $empExperience['from_date'] ?? null,
                        'to_date' => $empExperience['to_date'] ?? null,
                        'comment' => $empExperience['comment'] ?? null,
                    ]
                );
            }
        }

        return $employee;
    }


    public function updateAttachment(Employee $employee, array $data): Employee
    {
        if (!empty($data['attachments'])) {

            foreach ($data['attachments'] as $attachment) {

                $filePath = $attachmentModel->file_path ?? null;

                if (!empty($attachment['file'])) {

                    $filePath = $this->imageUpload->fileUpload(
                        file: $attachment['file'],
                        folder: 'employee/attachments',
                        data: $employee->attachments()->find($attachment['id']),
                        property: 'file_path',
                    );
                }

                $employee->attachments()->updateOrCreate(
                    [
                        'id' => $attachment['id'] ?? null
                    ],
                    [
                        'file_name' => $attachment['file_name'],
                        'file_path' => $filePath,
                        'is_visible' => $attachment['is_visible'] ?? true,
                    ]
                );
            }
        }

        return $employee;
    }


    public function updateEmProfilePicture(Employee $employee, array $data): Employee
    {
        if (!empty($data['image'])) {
            $filePath = $this->imageUpload->fileUpload(
                file: $data['image'],
                folder: 'employee/profile-picture',
                data: $employee,
                property: 'image',
                width: 300,
                height: 300,
            );
            $employee->update([
                'image' => $filePath
            ]);
        }

        return $employee;
    }

    public function findOrCreateEmployeeUser(Employee $employee, array $data): User
    {
        $user = User::where('email', $employee->email)->first();

        if ($user) {
            if ($user->isEmployeeUser()) {
                ApiValidationException::message(
                    'This email already belongs to another employee.'
                );
            }

            return $user;
        }

        $role = RolesEnum::EMPLOYEES->value;
        $user = $this->createUser([
            'name'     => $employee->name,
            'email'    => $employee->email,
            'phone'    => $employee->phone,
            'password' => $data['password'],
            'status'   => $data['status']
        ]);

        $this->updateUserId($user, $role);

        return $user;
    }

    public function attachUserToEmployee(Employee $employee, int $userId): void
    {
        $employee->update([
            'user_id' => $userId
        ]);
    }

    public function updateEmployeeAndTerminate(Employee $employee, array $data): Employee
    {
        $termination = $employee->terminationRecords()->create([
            'termination_id' => $data['termination_id'],
            'termination_date' => $data['termination_date'],
            'note' => $data['note'] ?? null,
        ]);

        if ($employee->hasUserAccount()) {
            $this->updateUserStatus($employee->user, StatusEnum::INACTIVE);
        }

        $employee->update([
            'emp_termination_id' => $termination->id
        ]);

        return $employee->load('terminationRecord');
    }

    public function updateUserStatus(User $user, $status = StatusEnum::INACTIVE): void
    {
        $user->update([
            'status' => $status
        ]);
    }

    public function updateEmployeeAndUnlinkTerminate(Employee $employee, array $data): Employee
    {
        if ($employee->hasUserAccount()) {
            $this->updateUserStatus($employee->user, $data['user_status']);
        }

        $employee->update([
            'emp_termination_id' => null
        ]);

        return $employee->fresh();
    }

    public function updateCurrentTermination(EmpTermination $empTermination, array $data)
    {
        $empTermination->update($data);

        return $empTermination->load('termination');
    }

    public function updateCurrentSalary(
        int $employeeId,
        float $salary,
        ?int $designationId = null
    ) {
        $data = [
            'current_basic_salary' => $salary,
        ];

        if ($designationId) {
            $data['designation_id'] = $designationId;
        }
        return Employee::where('id', $employeeId)->update($data);
    }
}
