<?php

namespace Modules\Scheduling\Services;

use App\Models\AttendanceShiftModel;
use App\Models\CompanyModel;
use App\Models\ContractEmployeeModel;
use App\Models\ContractModel;
use App\Models\EmployeeModel;
use App\Models\EmployeeShiftAssignmentModel;

class SchedulingService
{
    protected ContractEmployeeModel $assignments;
    protected ContractModel $contracts;
    protected CompanyModel $companies;
    protected EmployeeModel $employees;
    protected EmployeeShiftAssignmentModel $shiftAssignments;
    protected AttendanceShiftModel $shifts;

    public function __construct()
    {
        $this->assignments      = new ContractEmployeeModel();
        $this->contracts        = new ContractModel();
        $this->companies        = new CompanyModel();
        $this->employees        = new EmployeeModel();
        $this->shiftAssignments = new EmployeeShiftAssignmentModel();
        $this->shifts           = new AttendanceShiftModel();
    }

    /**
     * Active employees only — scheduling is restricted to the employee directory.
     *
     * @return list<array<string, mixed>>
     */
    public function activeEmployees(): array
    {
        return $this->employees
            ->where('status', 'active')
            ->orderBy('full_name', 'ASC')
            ->findAll();
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function activeCompanies(?string $search = null): array
    {
        return $this->companies
            ->scopeForList($search ?: null, 'active')
            ->orderBy('name', 'ASC')
            ->findAll();
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function contractsForCompany(int $companyId, ?string $status = null): array
    {
        $builder = $this->contracts
            ->scopeForList(null, $status, null, $companyId)
            ->orderBy('contracts.start_date', 'DESC');

        return $builder->findAll();
    }

    /**
     * @return array{success: bool, errors?: array<string, string>, id?: int|false}
     */
    public function assignEmployeeToContract(
        int $contractId,
        int $employeeId,
        ?string $role = null,
        ?float $hours = null
    ): array {
        $contract = $this->contracts->find($contractId);
        if (! $contract) {
            return ['success' => false, 'errors' => ['contract_id' => 'Contract not found.']];
        }

        $employee = $this->employees->find($employeeId);
        if (! $employee) {
            return ['success' => false, 'errors' => ['employee_id' => 'Employee not found. Only employees from the HR directory can be scheduled.']];
        }

        if (($employee['status'] ?? '') !== 'active') {
            return ['success' => false, 'errors' => ['employee_id' => 'Only active employees can be scheduled.']];
        }

        if ($this->assignments->isAssigned($contractId, $employeeId)) {
            return ['success' => false, 'errors' => ['employee_id' => 'This employee is already assigned to the contract.']];
        }

        $id = $this->assignments->assignEmployee($contractId, $employeeId, $role, $hours);
        if ($id === false) {
            return ['success' => false, 'errors' => ['employee_id' => 'Could not assign employee.']];
        }

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

    /**
     * @return array{success: bool, errors?: array<string, string>}
     */
    public function removeAssignment(int $assignmentId): array
    {
        $row = $this->assignments->find($assignmentId);
        if (! $row) {
            return ['success' => false, 'errors' => ['id' => 'Assignment not found.']];
        }

        $this->assignments->delete($assignmentId);

        return ['success' => true];
    }

    /**
     * @return array{success: bool, errors?: array<string, string>, id?: int|false}
     */
    public function assignShift(array $input, int $userId): array
    {
        $employeeId = (int) ($input['employee_id'] ?? 0);
        $shiftId    = (int) ($input['shift_id'] ?? 0);
        $from       = trim((string) ($input['effective_from'] ?? ''));
        $to         = trim((string) ($input['effective_to'] ?? ''));

        $employee = $this->employees->find($employeeId);
        if (! $employee || ($employee['status'] ?? '') !== 'active') {
            return ['success' => false, 'errors' => ['employee_id' => 'Select an active employee.']];
        }

        $shift = $this->shifts->find($shiftId);
        if (! $shift || ($shift['status'] ?? '') !== 'active') {
            return ['success' => false, 'errors' => ['shift_id' => 'Select an active shift.']];
        }

        if ($from === '' || ! preg_match('/^\d{4}-\d{2}-\d{2}$/', $from)) {
            return ['success' => false, 'errors' => ['effective_from' => 'Effective from date is required.']];
        }

        if ($to !== '' && $to < $from) {
            return ['success' => false, 'errors' => ['effective_to' => 'Effective to must be on or after the start date.']];
        }

        $id = $this->shiftAssignments->insert([
            'employee_id'    => $employeeId,
            'shift_id'       => $shiftId,
            'effective_from' => $from,
            'effective_to'   => $to !== '' ? $to : null,
            'created_by'     => $userId,
            'updated_by'     => $userId,
        ]);

        if ($id === false) {
            return ['success' => false, 'errors' => ['employee_id' => 'Could not create shift plan.']];
        }

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

    /**
     * @return array{success: bool, errors?: array<string, string>}
     */
    public function removeShiftAssignment(int $id): array
    {
        $row = $this->shiftAssignments->find($id);
        if (! $row) {
            return ['success' => false, 'errors' => ['id' => 'Shift assignment not found.']];
        }

        $this->shiftAssignments->delete($id);

        return ['success' => true];
    }
}
