<?php

declare(strict_types=1);

namespace App\Services\Import;

use App\DTOs\Import\EmployeeRowDto;
use App\Enums\EmploymentStatus;
use App\Enums\Gender;
use App\Models\Department;
use App\Models\Employee;
use App\Models\Position;
use App\Models\Shift;
use App\Support\Countries;
use Carbon\Carbon;
use Illuminate\Support\Str;

class EmployeeImportTransformer
{
    /**
     * @return array<string, mixed>
     */
    public function toAttributes(EmployeeRowDto $dto): array
    {
        return [
            'employee_number' => $dto->employeeNumber !== '' ? $dto->employeeNumber : null,
            'full_name' => $dto->fullName,
            'national_id' => $this->resolveNationality($dto->nationalId),
            'gender' => $this->resolveGender($dto->gender),
            'birth_date' => $this->parseDate($dto->birthDate),
            'phone' => $dto->phone ?? config('hr.default_phone'),
            'address' => $dto->address,
            'department_id' => $this->resolveDepartmentId($dto->department),
            'position_id' => $this->resolvePositionId($dto->position),
            'shift_id' => $this->resolveShiftId($dto->shift),
            'employment_status' => $this->resolveEmploymentStatus($dto->employmentStatus),
            'hire_date' => $this->parseDate($dto->hireDate),
            'notes' => $dto->notes,
        ];
    }

    /**
     * @return array<int, string>
     */
    public function validate(EmployeeRowDto $dto): array
    {
        $errors = [];

        if ($dto->fullName === '') {
            $errors[] = 'Full name is required.';
        }

        if ($dto->department && $this->resolveDepartmentId($dto->department) === null) {
            $errors[] = "Department \"{$dto->department}\" was not found.";
        }

        if ($dto->position && $this->resolvePositionId($dto->position) === null) {
            $errors[] = "Position \"{$dto->position}\" was not found.";
        }

        if ($dto->shift && $this->resolveShiftId($dto->shift) === null) {
            $errors[] = "Shift \"{$dto->shift}\" was not found.";
        }

        if ($dto->nationalId && $this->resolveNationality($dto->nationalId) === null) {
            $errors[] = "Nationality \"{$dto->nationalId}\" was not found.";
        }

        return $errors;
    }

    public function isDuplicate(EmployeeRowDto $dto): bool
    {
        $hasEmployeeNumber = $dto->employeeNumber !== '';
        $nationality = $this->resolveNationality($dto->nationalId);

        if (! $hasEmployeeNumber && $nationality === null) {
            return false;
        }

        return Employee::query()
            ->where(function ($query) use ($dto, $hasEmployeeNumber, $nationality): void {
                if ($hasEmployeeNumber) {
                    $query->where('employee_number', $dto->employeeNumber);
                }

                if ($nationality !== null) {
                    $hasEmployeeNumber
                        ? $query->orWhere('national_id', $nationality)
                        : $query->where('national_id', $nationality);
                }
            })
            ->exists();
    }

    private function resolveNationality(?string $value): ?string
    {
        return Countries::resolve($value);
    }

    private function resolveGender(?string $value): ?Gender
    {
        if ($value === null) {
            return null;
        }

        $normalized = Str::lower(trim($value));

        return Gender::tryFrom($normalized)
            ?? match ($normalized) {
                'm', 'man' => Gender::Male,
                'f', 'woman' => Gender::Female,
                default => null,
            };
    }

    private function resolveEmploymentStatus(?string $value): EmploymentStatus
    {
        if ($value === null || trim($value) === '') {
            return EmploymentStatus::Active;
        }

        $normalized = Str::lower(str_replace(' ', '_', trim($value)));

        return EmploymentStatus::tryFrom($normalized) ?? EmploymentStatus::Active;
    }

    private function resolveDepartmentId(?string $value): ?int
    {
        if ($value === null || trim($value) === '') {
            return null;
        }

        return Department::query()
            ->where('name', $value)
            ->orWhere('code', $value)
            ->value('id');
    }

    private function resolvePositionId(?string $value): ?int
    {
        if ($value === null || trim($value) === '') {
            return null;
        }

        return Position::query()->where('name', $value)->value('id');
    }

    private function resolveShiftId(?string $value): ?int
    {
        if ($value === null || trim($value) === '') {
            return null;
        }

        return Shift::query()
            ->where('name', $value)
            ->orWhere('code', $value)
            ->value('id');
    }

    private function parseDate(?string $value): ?string
    {
        if ($value === null || trim($value) === '') {
            return null;
        }

        try {
            return Carbon::parse($value)->toDateString();
        } catch (\Throwable) {
            return null;
        }
    }
}
