<?php

namespace App\Models;

use CodeIgniter\Model;

class ContractEmployeeModel extends Model
{
    protected $table         = 'contract_employees';
    protected $primaryKey    = 'id';
    protected $returnType    = 'array';
    protected $useTimestamps = false;

    protected $allowedFields = [
        'contract_id', 'employee_id', 'role_on_contract', 'hours_allocated', 'transport_leg', 'created_at',
    ];

    public function getForContract(int $contractId): array
    {
        return $this->select('contract_employees.*, employees.full_name, employees.employee_number, employees.job_title')
            ->join('employees', 'employees.id = contract_employees.employee_id', 'left')
            ->where('contract_employees.contract_id', $contractId)
            ->orderBy('employees.full_name', 'ASC')
            ->findAll();
    }

    /**
     * List employee↔contract assignments with company/contract context.
     * Employees only (joined to employees table).
     *
     * @return list<array<string, mixed>>
     */
    public function scopeForScheduling(
        ?string $search = null,
        ?int $companyId = null,
        ?int $contractId = null,
        ?int $employeeId = null
    ): self {
        $this->select('contract_employees.*, employees.full_name, employees.employee_number, employees.job_title, employees.status AS employee_status, contracts.number AS contract_number, contracts.title AS contract_title, contracts.status AS contract_status, contracts.start_date, contracts.end_date, contracts.company_id, companies.name AS company_name')
            ->join('employees', 'employees.id = contract_employees.employee_id', 'inner')
            ->join('contracts', 'contracts.id = contract_employees.contract_id', 'inner')
            ->join('companies', 'companies.id = contracts.company_id', 'left')
            ->where('employees.deleted_at', null);

        if ($search) {
            $this->groupStart()
                ->like('employees.full_name', $search)
                ->orLike('employees.employee_number', $search)
                ->orLike('contracts.number', $search)
                ->orLike('contracts.title', $search)
                ->orLike('companies.name', $search)
                ->groupEnd();
        }

        if ($companyId) {
            $this->where('contracts.company_id', $companyId);
        }

        if ($contractId) {
            $this->where('contract_employees.contract_id', $contractId);
        }

        if ($employeeId) {
            $this->where('contract_employees.employee_id', $employeeId);
        }

        return $this;
    }

    /**
     * Assignments whose contracts overlap a date range (for calendar).
     *
     * @return list<array<string, mixed>>
     */
    public function getInDateRange(string $startDate, string $endDate): array
    {
        return $this->select('contract_employees.*, employees.full_name, employees.employee_number, contracts.number AS contract_number, contracts.title AS contract_title, contracts.start_date, contracts.end_date, companies.name AS company_name')
            ->join('employees', 'employees.id = contract_employees.employee_id', 'inner')
            ->join('contracts', 'contracts.id = contract_employees.contract_id', 'inner')
            ->join('companies', 'companies.id = contracts.company_id', 'left')
            ->where('employees.deleted_at', null)
            ->whereIn('contracts.status', ['active', 'draft'])
            ->groupStart()
                ->where('contracts.start_date <=', $endDate)
                ->groupStart()
                    ->where('contracts.end_date', null)
                    ->orWhere('contracts.end_date >=', $startDate)
                ->groupEnd()
            ->groupEnd()
            ->orderBy('contracts.start_date', 'ASC')
            ->findAll();
    }

    public function isAssigned(int $contractId, int $employeeId): bool
    {
        return $this->where('contract_id', $contractId)
            ->where('employee_id', $employeeId)
            ->countAllResults() > 0;
    }

    public const TRANSPORT_LEGS = ['both', 'outbound_only', 'return_only'];

    public function assignEmployee(
        int $contractId,
        int $employeeId,
        ?string $role = null,
        ?float $hours = null,
        string $transportLeg = 'both'
    ): int|false
    {
        if ($employeeId <= 0 || $contractId <= 0) {
            return false;
        }

        if ($this->isAssigned($contractId, $employeeId)) {
            return false;
        }

        if (! in_array($transportLeg, self::TRANSPORT_LEGS, true)) {
            $transportLeg = 'both';
        }

        return $this->insert([
            'contract_id'      => $contractId,
            'employee_id'      => $employeeId,
            'role_on_contract' => $role !== null && trim($role) !== '' ? trim($role) : null,
            'hours_allocated'  => $hours,
            'transport_leg'    => $transportLeg,
            'created_at'       => date('Y-m-d H:i:s'),
        ]);
    }

    public function replaceForContract(int $contractId, array $assignments): void
    {
        $this->where('contract_id', $contractId)->delete();
        $now = date('Y-m-d H:i:s');
        $seen = [];

        foreach ($assignments as $item) {
            $employeeId = (int) ($item['employee_id'] ?? 0);
            if ($employeeId <= 0 || isset($seen[$employeeId])) {
                continue;
            }
            $seen[$employeeId] = true;

            $hours = $item['hours_allocated'] ?? null;
            $leg   = (string) ($item['transport_leg'] ?? 'both');
            if (! in_array($leg, self::TRANSPORT_LEGS, true)) {
                $leg = 'both';
            }
            $this->insert([
                'contract_id'      => $contractId,
                'employee_id'      => $employeeId,
                'role_on_contract' => trim((string) ($item['role_on_contract'] ?? '')) ?: null,
                'hours_allocated'  => $hours !== null && $hours !== '' ? (float) $hours : null,
                'transport_leg'    => $leg,
                'created_at'       => $now,
            ]);
        }
    }
}
