<?php

declare(strict_types=1);

namespace App\Models;

use App\Business\MasterData\MasterDataDeleteGuard;
use App\Models\Concerns\GeneratesDberpCode;
use App\Models\Concerns\HasDberpAuditTrail;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class DberpEmployee extends Model
{
    use GeneratesDberpCode;
    use HasDberpAuditTrail;

    protected $table = 'dberp_employees';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'code',
        'name',
        'mobile',
        'email',
        'department_id',
        'position_id',
        'department_name',
        'position_name',
        'is_salesperson',
        'is_enabled',
        'sort_order',
        'remark',
    ];

    protected static function booted(): void
    {
        static::deleting(function (self $employee): void {
            app(MasterDataDeleteGuard::class)->ensureCanDelete($employee);
        });
    }

    protected function casts(): array
    {
        return [
            'department_id' => 'integer',
            'position_id' => 'integer',
            'is_salesperson' => 'boolean',
            'is_enabled' => 'boolean',
            'sort_order' => 'integer',
        ];
    }

    public function getDberpNumberScope(): string
    {
        return 'employee';
    }

    public function getDberpAuditActionPrefix(): string
    {
        return 'dberp.employee';
    }

    /**
     * @return list<string>
     */
    public function getDberpAuditFields(): array
    {
        return $this->getFillable();
    }

    public function department(): BelongsTo
    {
        return $this->belongsTo(DberpDepartment::class, 'department_id');
    }

    public function position(): BelongsTo
    {
        return $this->belongsTo(DberpPosition::class, 'position_id');
    }

    public function managedWarehouses(): HasMany
    {
        return $this->hasMany(DberpWarehouse::class, 'manager_employee_id');
    }

    public function salesCustomers(): HasMany
    {
        return $this->hasMany(DberpCustomer::class, 'salesperson_employee_id');
    }

    /**
     * @return HasMany<DberpSalesperson, $this>
     */
    public function salespersonProfiles(): HasMany
    {
        return $this->hasMany(DberpSalesperson::class, 'employee_id');
    }

    public function resolvedDepartmentLabel(): string
    {
        $department = $this->department;
        if ($department !== null && filled($department->name)) {
            return (string) $department->name;
        }

        if (filled($this->department_name)) {
            return (string) $this->department_name;
        }

        return (string) __('dberp.labels.master_not_set');
    }

    public function resolvedPositionLabel(): string
    {
        $position = $this->position;
        if ($position !== null && filled($position->name)) {
            return (string) $position->name;
        }

        if (filled($this->position_name)) {
            return (string) $this->position_name;
        }

        return (string) __('dberp.labels.master_not_set');
    }

    /**
     * 下拉展示：姓名后附部门、岗位（优先主档关联，其次文本快照）；无部门岗位时仅姓名。
     */
    public function labelForSelectWithOrganization(): string
    {
        $dept = $this->resolvedDepartmentLabel();
        $pos = $this->resolvedPositionLabel();
        $notSet = (string) __('dberp.labels.master_not_set');

        $segments = [];
        if ($dept !== $notSet) {
            $segments[] = $dept;
        }
        if ($pos !== $notSet) {
            $segments[] = $pos;
        }

        if ($segments === []) {
            return (string) $this->name;
        }

        return sprintf('%s（%s）', $this->name, implode(' / ', $segments));
    }

    /**
     * Filament 等下拉：职员 id => 展示标签（含部门/岗位）。
     *
     * @param  callable(Builder): mixed|null  $modifyQuery
     * @return array<int, string>
     */
    public static function selectOptionsForFilament(?callable $modifyQuery = null, ?int $preserveDisabledEmployeeId = null): array
    {
        $query = static::query()->with(['department', 'position']);

        if ($modifyQuery !== null) {
            $modifyQuery($query);
        }

        $table = (new static)->getTable();
        $query->orderBy($table.'.sort_order')->orderBy($table.'.id');

        $out = [];
        foreach ($query->cursor() as $employee) {
            /** @var self $employee */
            $out[(int) $employee->getKey()] = $employee->labelForSelectWithOrganization();
        }

        if ($preserveDisabledEmployeeId !== null && ! array_key_exists($preserveDisabledEmployeeId, $out)) {
            $extra = static::query()->with(['department', 'position'])->find($preserveDisabledEmployeeId);
            if ($extra !== null && ! $extra->is_enabled) {
                $out[$preserveDisabledEmployeeId] = sprintf(
                    '%s（%s）',
                    $extra->labelForSelectWithOrganization(),
                    __('dberp.labels.master_disabled_suffix'),
                );
            }
        }

        return $out;
    }

    /**
     * 职员表单：部门下拉（默认可选启用部门；编辑时可保留当前已选停用部门）。
     *
     * @return array<int, string>
     */
    public static function departmentSelectOptionsForEmployeeForm(?int $preserveDisabledDepartmentId): array
    {
        $out = [];

        foreach (DberpDepartment::query()->where('is_enabled', true)->orderBy('sort_order')->orderBy('id')->cursor() as $department) {
            $out[(int) $department->getKey()] = (string) $department->name;
        }

        if ($preserveDisabledDepartmentId !== null) {
            $extra = DberpDepartment::query()->find($preserveDisabledDepartmentId);
            if ($extra !== null && ! $extra->is_enabled) {
                $out[(int) $extra->getKey()] = sprintf('%s（%s）', $extra->name, __('dberp.labels.master_disabled_suffix'));
            }
        }

        return $out;
    }

    /**
     * 职员表单：岗位下拉（默认可选启用岗位；可选按部门过滤；通用岗位 department_id 为空始终可选）。
     *
     * @return array<int, string>
     */
    public static function positionSelectOptionsForEmployeeForm(
        ?int $filterByDepartmentId,
        ?int $preserveDisabledPositionId,
        ?int $mustIncludePositionIdFromForm,
    ): array {
        $out = [];

        $query = DberpPosition::query()
            ->where('is_enabled', true)
            ->when(
                $filterByDepartmentId !== null,
                fn ($q) => $q->where(function ($inner) use ($filterByDepartmentId): void {
                    $inner->whereNull('department_id')
                        ->orWhere('department_id', $filterByDepartmentId);
                }),
            )
            ->orderBy('sort_order')
            ->orderBy('id');

        foreach ($query->cursor() as $position) {
            $out[(int) $position->getKey()] = (string) $position->name;
        }

        $mergeExtra = function (?int $id, array &$target): void {
            if ($id === null) {
                return;
            }

            if (array_key_exists($id, $target)) {
                return;
            }

            $extra = DberpPosition::query()->find($id);
            if ($extra === null) {
                return;
            }

            $suffix = $extra->is_enabled ? '' : sprintf('（%s）', __('dberp.labels.master_disabled_suffix'));
            $target[$id] = (string) $extra->name.$suffix;
        };

        $mergeExtra($preserveDisabledPositionId, $out);
        $mergeExtra($mustIncludePositionIdFromForm, $out);

        return $out;
    }
}
