<?php

namespace App\Models\ItemMaster;

use App\Traits\Auditable;
use App\Traits\BelongsToTenant;
use App\Traits\ModelTrait;
use Illuminate\Database\Eloquent\Model;

/**
 * EntityRelationship - 엔티티 간 관계를 관리하는 링크 테이블 모델
 *
 * 지원하는 관계 유형:
 * - page-section: 페이지와 섹션 연결
 * - page-field: 페이지와 필드 직접 연결
 * - section-field: 섹션과 필드 연결
 * - section-bom: 섹션과 BOM 항목 연결
 */
class EntityRelationship extends Model
{
    use Auditable, BelongsToTenant, ModelTrait;

    protected $fillable = [
        'tenant_id',
        'group_id',
        'parent_type',
        'parent_id',
        'child_type',
        'child_id',
        'order_no',
        'metadata',
        'is_locked',
        'locked_by',
        'locked_at',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'group_id' => 'integer',
        'parent_id' => 'integer',
        'child_id' => 'integer',
        'order_no' => 'integer',
        'metadata' => 'array',
        'is_locked' => 'boolean',
        'locked_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    // 엔티티 타입 상수
    public const TYPE_PAGE = 'page';

    public const TYPE_SECTION = 'section';

    public const TYPE_FIELD = 'field';

    public const TYPE_BOM = 'bom';

    // 그룹 ID 상수
    public const GROUP_ITEM_MASTER = 1;

    /**
     * 부모 엔티티 조회 (Polymorphic)
     */
    public function parent()
    {
        return $this->morphTo('parent', 'parent_type', 'parent_id');
    }

    /**
     * 자식 엔티티 조회 (Polymorphic)
     */
    public function child()
    {
        return $this->morphTo('child', 'child_type', 'child_id');
    }

    /**
     * 부모 타입에 따른 모델 클래스 반환
     */
    public static function getModelClass(string $type): ?string
    {
        return match ($type) {
            self::TYPE_PAGE => ItemPage::class,
            self::TYPE_SECTION => ItemSection::class,
            self::TYPE_FIELD => ItemField::class,
            self::TYPE_BOM => ItemBomItem::class,
            default => null,
        };
    }

    /**
     * 특정 부모의 자식 관계 조회
     */
    public static function getChildren(string $parentType, int $parentId, ?string $childType = null)
    {
        $query = self::where('parent_type', $parentType)
            ->where('parent_id', $parentId)
            ->orderBy('order_no');

        if ($childType) {
            $query->where('child_type', $childType);
        }

        return $query;
    }

    /**
     * 특정 자식의 부모 관계 조회
     */
    public static function getParents(string $childType, int $childId, ?string $parentType = null)
    {
        $query = self::where('child_type', $childType)
            ->where('child_id', $childId);

        if ($parentType) {
            $query->where('parent_type', $parentType);
        }

        return $query;
    }

    /**
     * 관계 생성 또는 업데이트
     */
    public static function link(
        int $tenantId,
        string $parentType,
        int $parentId,
        string $childType,
        int $childId,
        int $orderNo = 0,
        ?array $metadata = null,
        int $groupId = self::GROUP_ITEM_MASTER
    ): self {
        return self::updateOrCreate(
            [
                'tenant_id' => $tenantId,
                'group_id' => $groupId,
                'parent_type' => $parentType,
                'parent_id' => $parentId,
                'child_type' => $childType,
                'child_id' => $childId,
            ],
            [
                'order_no' => $orderNo,
                'metadata' => $metadata,
            ]
        );
    }

    /**
     * 관계 해제
     *
     * @throws \App\Exceptions\BusinessException 잠금된 연결인 경우
     */
    public static function unlink(
        int $tenantId,
        string $parentType,
        int $parentId,
        string $childType,
        int $childId,
        int $groupId = self::GROUP_ITEM_MASTER
    ): bool {
        $relationship = self::where([
            'tenant_id' => $tenantId,
            'group_id' => $groupId,
            'parent_type' => $parentType,
            'parent_id' => $parentId,
            'child_type' => $childType,
            'child_id' => $childId,
        ])->first();

        if (! $relationship) {
            return false;
        }

        // 잠금 체크
        if ($relationship->is_locked) {
            throw new \App\Exceptions\BusinessException(__('error.relationship_locked'));
        }

        return $relationship->delete();
    }

    /**
     * 특정 부모의 모든 자식 관계 해제 (잠금되지 않은 것만)
     *
     * @throws \App\Exceptions\BusinessException 잠금된 연결이 있는 경우
     */
    public static function unlinkAllChildren(
        int $tenantId,
        string $parentType,
        int $parentId,
        ?string $childType = null,
        int $groupId = self::GROUP_ITEM_MASTER
    ): int {
        $query = self::where([
            'tenant_id' => $tenantId,
            'group_id' => $groupId,
            'parent_type' => $parentType,
            'parent_id' => $parentId,
        ]);

        if ($childType) {
            $query->where('child_type', $childType);
        }

        // 잠금된 연결이 있는지 확인
        if ($query->clone()->where('is_locked', true)->exists()) {
            throw new \App\Exceptions\BusinessException(__('error.has_locked_relationships'));
        }

        return $query->delete();
    }

    /**
     * 특정 자식 엔티티가 잠금된 연결을 가지고 있는지 확인
     */
    public static function hasLockedParentRelationship(string $childType, int $childId): bool
    {
        return self::where('child_type', $childType)
            ->where('child_id', $childId)
            ->where('is_locked', true)
            ->exists();
    }

    /**
     * 특정 자식 엔티티의 잠금 상태 조회 (computed)
     * 연결이 잠겨있으면 엔티티도 잠금 상태로 간주
     */
    public static function getChildLockStatus(string $childType, int $childId): array
    {
        $lockedRelationships = self::where('child_type', $childType)
            ->where('child_id', $childId)
            ->where('is_locked', true)
            ->get(['id', 'parent_type', 'parent_id']);

        return [
            'is_locked' => $lockedRelationships->isNotEmpty(),
            'locked_by_relationships' => $lockedRelationships->map(fn ($rel) => [
                'relationship_id' => $rel->id,
                'parent_type' => $rel->parent_type,
                'parent_id' => $rel->parent_id,
            ])->toArray(),
        ];
    }
}
