<?php

namespace App\Domain\Crew\Models;

use App\Domain\Agent\Models\Agent;
use App\Domain\AgentChatProtocol\Models\ExternalAgent;
use App\Domain\Crew\Enums\CrewMemberRole;
use Database\Factories\Domain\Crew\CrewMemberFactory;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Carbon;

/**
 * @property string $id
 * @property string $crew_id
 * @property string|null $agent_id
 * @property string|null $external_agent_id
 * @property string|null $member_kind
 * @property CrewMemberRole $role
 * @property int $sort_order
 * @property array<string, mixed>|null $config
 * @property array<int, string>|null $context_scope
 * @property Carbon|null $created_at
 * @property Carbon|null $updated_at
 * @property-read Crew $crew
 * @property-read Agent|null $agent
 * @property-read ExternalAgent|null $externalAgent
 */
class CrewMember extends Model
{
    use HasFactory, HasUuids;

    protected static function newFactory()
    {
        return CrewMemberFactory::new();
    }

    protected $fillable = [
        'crew_id',
        'agent_id',
        'external_agent_id',
        'member_kind',
        'role',
        'sort_order',
        'config',
        'context_scope',
    ];

    protected function casts(): array
    {
        return [
            'role' => CrewMemberRole::class,
            'config' => 'array',
            'context_scope' => 'array',
            'sort_order' => 'integer',
        ];
    }

    /** @return BelongsTo<Crew, $this> */
    public function crew(): BelongsTo
    {
        return $this->belongsTo(Crew::class);
    }

    /** @return BelongsTo<Agent, $this> */
    public function agent(): BelongsTo
    {
        return $this->belongsTo(Agent::class);
    }

    /** @return BelongsTo<ExternalAgent, $this> */
    public function externalAgent(): BelongsTo
    {
        return $this->belongsTo(ExternalAgent::class);
    }

    public function isExternal(): bool
    {
        return ($this->member_kind ?? 'internal') === 'external' || $this->external_agent_id !== null;
    }

    /**
     * Tool IDs this crew member is allowed to use (BroodMind worker permission template).
     * Empty array means no restriction — all agent tools are available.
     *
     * @return string[]
     */
    public function allowedToolIds(): array
    {
        return (array) ($this->config['tool_allowlist'] ?? []);
    }

    /**
     * Structured constraints for this crew member's execution context.
     *
     * @return array<string, mixed>
     */
    public function constraints(): array
    {
        return (array) ($this->config['constraints'] ?? []);
    }

    /**
     * Tool name allowlist for this crew member.
     * null means unrestricted — all agent tools are available.
     * Non-empty array restricts execution to only tools whose name is in the list.
     *
     * @return string[]|null
     */
    public function getToolAllowlistAttribute(): ?array
    {
        $list = $this->config['tool_allowlist'] ?? null;
        if ($list === null || $list === []) {
            return null;
        }

        return (array) $list;
    }

    /**
     * Maximum number of LLM tool-call steps allowed for this crew member.
     * null means use the agent's default tier configuration.
     */
    public function getMaxStepsAttribute(): ?int
    {
        $value = $this->config['max_steps'] ?? null;

        return $value !== null ? (int) $value : null;
    }

    /**
     * Maximum credits this crew member may spend per execution.
     * null means no per-member credit cap (agent budget applies).
     */
    public function getMaxCreditsAttribute(): ?int
    {
        $value = $this->config['max_credits'] ?? null;

        return $value !== null ? (int) $value : null;
    }

    /**
     * Look up the CrewMember row for a given agent within a specific crew,
     * if one exists. Returns null when the agent is not enrolled in the crew
     * (e.g., legacy executions whose qa_agent / coordinator predates the
     * members table population).
     */
    public static function forAgentInCrew(?string $agentId, ?string $crewId): ?self
    {
        if ($agentId === null || $crewId === null) {
            return null;
        }

        return static::query()
            ->where('crew_id', $crewId)
            ->where('agent_id', $agentId)
            ->first();
    }

    /**
     * Per-role model override resolved from config JSONB.
     * Returns ['provider' => string, 'model' => string] or null when unset.
     *
     * @return array{provider: string, model: string}|null
     */
    public function getModelOverrideAttribute(): ?array
    {
        $override = $this->config['model_override'] ?? null;
        if (! is_array($override)) {
            return null;
        }
        $provider = $override['provider'] ?? null;
        $model = $override['model'] ?? null;
        if (! is_string($provider) || $provider === '' || ! is_string($model) || $model === '') {
            return null;
        }

        return ['provider' => $provider, 'model' => $model];
    }
}
