<?php

namespace Modules\Employee\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Modules\Auth\Services\RoleAssignmentService;
use Modules\Core\Enums\RolesEnum;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Employee\DTO\EmployeeListFilter;
use Modules\Employee\Models\Employee;
use Modules\Employee\Repositories\EmployeeRepository;

class EmployeeService
{
    protected $employeeRepo;
    protected $roleAssignmentService;

    public function __construct(
        EmployeeRepository $employeeRepo,
        RoleAssignmentService $roleAssignmentService
    ){
        $this->employeeRepo = $employeeRepo;
        $this->roleAssignmentService = $roleAssignmentService;
    }

    public function list(EmployeeListFilter $filters)
    {
        $columns = [
            'id',
            'emp_number',
            'name',
            'phone',
            'email',
            'image',
            'department_id',
            'designation_id',
            'emp_termination_id'
        ];

        return $this->employeeRepo->getEmployees($filters, $columns);
    }

    public function store(array $data)
    {
        return DB::transaction(function () use ($data) {
            $userId = null;

            if (isset($data['createLogin']) && $data['createLogin']) {
                $role = RolesEnum::EMPLOYEES->value;
                $user = $this->employeeRepo->createUser($data);

                $this->roleAssignmentService->assign($user, $role);
                $this->employeeRepo->updateUserId($user, $role);

                $userId = $user->id;
            }

            return $this->employeeRepo->createEmployee([
                'name'       => $data['name'],
                'email'      => $data['email'],
                'phone'      => $data['phone'],
                'emp_number' => $data['emp_number'] ?? null,
            ], $userId);
        });
    }

    public function find(int $id): Employee
    {
        return $this->employeeRepo->find($id);
    }

    public function update(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmployee($employee, $data);
        });
    }
    public function updateEmpContact(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpContact($employee, $data);
        });
    }

    public function updateEmpTaxProfile(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpEmpTaxProfile($employee, $data);
        });
    }

    public function updateEmpEmergencyContact(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpEmergencyContact($employee, $data);
        });
    }

    public function updateEmpJobDetails(Employee $employee, array $data)
    {
        if (
            !empty($data['report_to']) &&
            (int) $data['report_to'] === (int) $employee->id
        ) {
            ApiValidationException::errors([
                'report_to' => [
                    'Employee cannot report to themselves.'
                ]
            ]);
        }

        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpJobDetails(
                $employee,
                $data
            );
        });
    }


    public function updateEmpSalary(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpSalary($employee, $data);
        });
    }

    public function updateEmpPayroll(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpPayroll($employee, $data);
        });
    }

    public function updateEmpEducations(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpEducations($employee, $data);
        });
    }

    public function updateEmpExperiences(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmpExperiences($employee, $data);
        });
    }

    public function updateAttachment(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateAttachment($employee, $data);
        });
    }
    public function updateEmProfilePicture(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            return $this->employeeRepo->updateEmProfilePicture($employee, $data);
        });
    }

    public function destroyEmpEmergencyContact(Employee $employee, array $data)
    {
        $ids = $data['ids'] ?? [];

        DB::transaction(function () use ($employee, $ids) {
            $employee->emergencies()->whereIn('id', $ids)->delete();
        });

        return true;
    }

    public function destroyEmpEducation(Employee $employee, array $data)
    {
        $ids = $data['ids'] ?? [];

        DB::transaction(function () use ($employee, $ids) {
            $employee->educations()->whereIn('id', $ids)->delete();
        });

        return true;
    }

    public function destroyEmpExperience(Employee $employee, array $data)
    {
        $ids = $data['ids'] ?? [];

        DB::transaction(function () use ($employee, $ids) {
            $employee->experiences()->whereIn('id', $ids)->delete();
        });

        return true;
    }

    public function destroyEmpAttachment(Employee $employee, array $data)
    {
        $ids = $data['ids'] ?? [];

        DB::transaction(function () use ($employee, $ids) {
            $employee->attachments()->whereIn('id', $ids)->delete();
        });

        return true;
    }

    public function terminate(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            if ($employee->isTerminated()) {
                ApiValidationException::message(
                    'Employee already terminated.'
                );
            }

            return $this->employeeRepo->updateEmployeeAndTerminate($employee, $data);
        });
    }

    public function reactivate(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            if ($employee->isActive()) {
                ApiValidationException::message(
                    'Employee already active.'
                );
            }

            return $this->employeeRepo->updateEmployeeAndUnlinkTerminate($employee, $data);
        });
    }

    public function getCurrentTermination(Employee $employee)
    {
        if ($employee->isTerminated()) {
            $termination = $employee->load('terminationRecord.termination');

            return [
                'message' => 'Termination details fetched successfully.',
                'data' => $termination?->terminationRecord
            ];
        }

        return [
            'message' => 'Employee already active. No termination record found.',
            'data' => null
        ];
    }

    public function updateTermination(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            if ($employee->isActive()) {
                ApiValidationException::message(
                    'Active employee cannot have termination updated.'
                );
            }

            $employee->load('terminationRecord');

            if (!$employee->terminationRecord) {
                ApiValidationException::message(
                    'Termination record not found for this employee.'
                );
            }

            return $this->employeeRepo->updateCurrentTermination(
                $employee->terminationRecord,
                $data
            );
        });
    }

    public function createEmployeeLogin(Employee $employee, array $data)
    {
        return DB::transaction(function () use ($employee, $data) {
            if ($employee->hasUserAccount()) {
                ApiValidationException::message(
                    'Employee already has login user.'
                );
            }

            $user = $this->employeeRepo->findOrCreateEmployeeUser($employee, $data);
            $role = RolesEnum::EMPLOYEES->value;

            $this->roleAssignmentService->assign($user, $role);
            $this->employeeRepo->attachUserToEmployee($employee, $user->id);

            return $employee->fresh()->load('user');
        });
    }
}
