<?php

namespace Modules\Employees\Services;

use App\Models\ActivityLogModel;
use App\Models\EmployeeModel;
use CodeIgniter\HTTP\Files\UploadedFile;
use Config\Uploads as UploadsConfig;

class EmployeeService
{
    protected EmployeeModel $employees;
    protected ActivityLogModel $activity;
    protected UploadsConfig $uploadsConfig;

    public function __construct()
    {
        $this->employees     = new EmployeeModel();
        $this->activity      = new ActivityLogModel();
        $this->uploadsConfig = config('Uploads');
    }

    public function generateEmployeeNumber(): string
    {
        $year = (int) date('Y');
        $seq  = $this->employees->nextSequenceForYear($year);

        return sprintf('EMP-%d-%04d', $year, $seq);
    }

    public function resolveEmployeeNumber(array $input): array
    {
        $useCustom = ! empty($input['use_custom_id']);
        $custom    = trim((string) ($input['employee_number'] ?? ''));

        if ($useCustom) {
            if ($custom === '') {
                return ['success' => false, 'errors' => ['employee_number' => 'Custom employee ID is required when enabled.']];
            }
            if ($this->employees->isEmployeeNumberTaken($custom)) {
                return ['success' => false, 'errors' => ['employee_number' => 'That employee ID is already in use.']];
            }

            return ['success' => true, 'number' => strtoupper($custom)];
        }

        $auto = $this->generateEmployeeNumber();
        while ($this->employees->isEmployeeNumberTaken($auto)) {
            $parts = explode('-', $auto);
            $seq   = (int) end($parts) + 1;
            $auto  = sprintf('EMP-%d-%04d', (int) date('Y'), $seq);
        }

        return ['success' => true, 'number' => $auto];
    }

    public function create(array $input, ?int $actingUserId, ?UploadedFile $photo = null, ?UploadedFile $cprDoc = null): array
    {
        $numberResult = $this->resolveEmployeeNumber($input);
        if (! $numberResult['success']) {
            return $numberResult;
        }

        $cpr = trim((string) ($input['cpr_number'] ?? ''));
        if ($cpr !== '' && $this->employees->isCprTaken($cpr)) {
            return ['success' => false, 'errors' => ['cpr_number' => 'That CPR number is already registered to another employee.']];
        }

        $data = $this->mapInputToFields($input, $actingUserId);
        $data['employee_number'] = $numberResult['number'];
        $data['created_by']      = $actingUserId;
        $data['updated_by']      = $actingUserId;

        if (! $this->employees->insert($data, true)) {
            return ['success' => false, 'errors' => $this->employees->errors()];
        }

        $id = $this->employees->getInsertID();

        if ($photo && $photo->isValid() && ! $photo->hasMoved()) {
            $this->uploadPhoto($id, $photo);
        }

        if ($cprDoc && $cprDoc->isValid() && ! $cprDoc->hasMoved()) {
            $this->uploadCprDocument($id, $cprDoc);
        }

        $this->activity->log('employees', 'created', $actingUserId, "Created employee #{$id} ({$data['full_name']})", (string) $id);

        return ['success' => true, 'id' => $id, 'employee_number' => $data['employee_number']];
    }

    public function update(int $id, array $input, ?int $actingUserId, ?UploadedFile $photo = null, ?UploadedFile $cprDoc = null): array
    {
        $employee = $this->employees->withDeleted()->find($id);
        if (! $employee) {
            return ['success' => false, 'errors' => ['id' => 'Employee not found.']];
        }

        $useCustom = ! empty($input['use_custom_id']);
        $custom    = trim((string) ($input['employee_number'] ?? ''));

        if ($useCustom) {
            if ($custom === '') {
                return ['success' => false, 'errors' => ['employee_number' => 'Employee ID is required.']];
            }
            if ($this->employees->isEmployeeNumberTaken(strtoupper($custom), $id)) {
                return ['success' => false, 'errors' => ['employee_number' => 'That employee ID is already in use.']];
            }
        }

        $cpr = trim((string) ($input['cpr_number'] ?? ''));
        if ($cpr !== '' && $this->employees->isCprTaken($cpr, $id)) {
            return ['success' => false, 'errors' => ['cpr_number' => 'That CPR number is already registered to another employee.']];
        }

        $data = $this->mapInputToFields($input, $actingUserId);
        $data['updated_by'] = $actingUserId;

        if ($useCustom) {
            $data['employee_number'] = strtoupper($custom);
        }

        if (! $this->employees->update($id, $data)) {
            return ['success' => false, 'errors' => $this->employees->errors()];
        }

        if ($photo && $photo->isValid() && ! $photo->hasMoved()) {
            $this->uploadPhoto($id, $photo);
        }

        if ($cprDoc && $cprDoc->isValid() && ! $cprDoc->hasMoved()) {
            $this->uploadCprDocument($id, $cprDoc);
        }

        $this->activity->log('employees', 'updated', $actingUserId, "Updated employee #{$id}", (string) $id);

        return ['success' => true];
    }

    public function activate(int $id, ?int $actingUserId): void
    {
        $this->employees->update($id, ['status' => 'active']);
        $this->activity->log('employees', 'activated', $actingUserId, "Activated employee #{$id}", (string) $id);
    }

    public function deactivate(int $id, ?int $actingUserId): void
    {
        $this->employees->update($id, ['status' => 'inactive']);
        $this->activity->log('employees', 'deactivated', $actingUserId, "Deactivated employee #{$id}", (string) $id);
    }

    public function softDelete(int $id, ?int $actingUserId): void
    {
        $this->employees->delete($id);
        $this->activity->log('employees', 'deleted', $actingUserId, "Soft-deleted employee #{$id}", (string) $id);
    }

    public function restore(int $id, ?int $actingUserId): void
    {
        $this->employees->restore($id);
        $this->activity->log('employees', 'restored', $actingUserId, "Restored employee #{$id}", (string) $id);
    }

    public function uploadPhoto(int $id, UploadedFile $file): array
    {
        return $this->handleImageUpload($id, $file, 'photo_path', 'employeePhoto');
    }

    public function uploadCprDocument(int $id, UploadedFile $file): array
    {
        if (! $file->isValid() || $file->hasMoved()) {
            return ['success' => false, 'error' => 'Upload failed. Please try again.'];
        }

        $allowedMimes = array_merge($this->uploadsConfig->employeePhotoAllowedMimes, ['application/pdf']);
        if (! in_array($file->getMimeType(), $allowedMimes, true)) {
            return ['success' => false, 'error' => 'Only JPEG, PNG, WEBP or PDF files are allowed.'];
        }

        if ($file->getSizeByUnit('kb') > $this->uploadsConfig->employeeDocumentMaxSizeKB) {
            return ['success' => false, 'error' => 'File must be smaller than ' . $this->uploadsConfig->employeeDocumentMaxSizeKB . 'KB.'];
        }

        $newName = $file->getRandomName();
        $path    = $this->uploadsConfig->employeeDocumentUploadPath;

        if (! is_dir($path)) {
            mkdir($path, 0755, true);
        }

        $file->move($path, $newName);
        $relativePath = rtrim($this->uploadsConfig->employeeDocumentPublicPath, '/') . '/' . $newName;

        $employee = $this->employees->find($id);
        if (! empty($employee['cpr_document_path'])) {
            $oldPath = FCPATH . ltrim($employee['cpr_document_path'], '/');
            if (is_file($oldPath)) {
                @unlink($oldPath);
            }
        }

        $this->employees->update($id, ['cpr_document_path' => $relativePath]);

        return ['success' => true, 'path' => $relativePath];
    }

    protected function handleImageUpload(int $id, UploadedFile $file, string $field, string $type): array
    {
        if (! $file->isValid() || $file->hasMoved()) {
            return ['success' => false, 'error' => 'Upload failed. Please try again.'];
        }

        $maxKb    = $this->uploadsConfig->employeePhotoMaxSizeKB;
        $mimes    = $this->uploadsConfig->employeePhotoAllowedMimes;
        $diskPath = $this->uploadsConfig->employeePhotoUploadPath;
        $pubPath  = $this->uploadsConfig->employeePhotoPublicPath;

        if (! in_array($file->getMimeType(), $mimes, true)) {
            return ['success' => false, 'error' => 'Only JPEG, PNG or WEBP images are allowed.'];
        }

        if ($file->getSizeByUnit('kb') > $maxKb) {
            return ['success' => false, 'error' => 'Image must be smaller than ' . $maxKb . 'KB.'];
        }

        $newName = $file->getRandomName();
        if (! is_dir($diskPath)) {
            mkdir($diskPath, 0755, true);
        }

        $file->move($diskPath, $newName);
        $relativePath = rtrim($pubPath, '/') . '/' . $newName;

        $employee = $this->employees->find($id);
        if (! empty($employee[$field])) {
            $oldPath = FCPATH . ltrim($employee[$field], '/');
            if (is_file($oldPath)) {
                @unlink($oldPath);
            }
        }

        $this->employees->update($id, [$field => $relativePath]);

        return ['success' => true, 'path' => $relativePath];
    }

    protected function mapInputToFields(array $input, ?int $actingUserId): array
    {
        return [
            'full_name'                 => trim($input['full_name']),
            'department_id'             => $input['department_id'] ?: null,
            'branch_id'                 => $input['branch_id'] ?: null,
            'job_title'                 => $input['job_title'] ?: null,
            'email'                     => $input['email'] ?: null,
            'phone'                     => $input['phone'] ?: null,
            'mobile'                    => $input['mobile'] ?: null,
            'cpr_number'                => $input['cpr_number'] ?: null,
            'nationality'               => $input['nationality'] ?: null,
            'date_of_birth'             => $input['date_of_birth'] ?: null,
            'gender'                    => $input['gender'] ?: null,
            'marital_status'            => $input['marital_status'] ?: null,
            'address'                   => $input['address'] ?: null,
            'city'                      => $input['city'] ?: null,
            'country'                   => $input['country'] ?: 'Bahrain',
            'emergency_contact_name'    => $input['emergency_contact_name'] ?: null,
            'emergency_contact_phone'   => $input['emergency_contact_phone'] ?: null,
            'hire_date'                 => $input['hire_date'] ?: null,
            'employment_type'           => $input['employment_type'] ?? 'full_time',
            'bank_name'                 => $input['bank_name'] ?: null,
            'bank_account'              => $input['bank_account'] ?: null,
            'notes'                     => $input['notes'] ?: null,
            'status'                    => $input['status'] ?? 'active',
        ];
    }
}
