<?php
/**
 * Entity Block Model
 * Stores block data for any entity type (page, item, category)
 */
namespace Mtc\Modules\BlockRegistry\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Builder;
use Mtc\Modules\BlockRegistry\Services\EntityBlockService;

class EntityBlock extends Model
{
    protected $table = 'entity_blocks';

    protected $fillable = [
        'entity_type',
        'entity_id',
        'block_id',
        'order',
        'data',
        'structure_hash',
        'status',
        'source_block_id',
        'name',
        'preview_image',
    ];

    /**
     * Status constants
     */
    const STATUS_DRAFT = 'draft';
    const STATUS_PUBLISHED = 'published';

    protected $casts = [
        'entity_id' => 'integer',
        'block_id' => 'integer',
        'order' => 'integer',
        'data' => 'array',
    ];

    /**
     * Get the block definition from the registry
     */
    public function block(): BelongsTo
    {
        return $this->belongsTo(BlockRegistry::class, 'block_id');
    }

    /**
     * Get the source global block (for references)
     */
    public function sourceBlock(): BelongsTo
    {
        return $this->belongsTo(self::class, 'source_block_id');
    }

    /**
     * Get all references pointing to this global block
     */
    public function references(): HasMany
    {
        return $this->hasMany(self::class, 'source_block_id');
    }

    /**
     * Scope: Filter to global blocks only (entity_type = 'global')
     */
    public function scopeGlobal(Builder $query): Builder
    {
        return $query->where('entity_type', 'global')->whereNull('source_block_id');
    }

    /**
     * Check if this is a global block (the source)
     */
    public function isGlobalBlock(): bool
    {
        return $this->entity_type === 'global' && $this->source_block_id === null;
    }

    /**
     * Check if this is a reference to a global block
     */
    public function isGlobalReference(): bool
    {
        return $this->source_block_id !== null;
    }

    /**
     * Get effective data: own data for regular blocks, source data for references
     */
    public function getEffectiveData(): ?array
    {
        if ($this->isGlobalReference()) {
            $source = $this->sourceBlock;
            return $source ? $source->data : null;
        }
        return $this->data;
    }

    /**
     * Get display name: name for global blocks, block type name for regular blocks
     */
    public function getDisplayNameAttribute(): string
    {
        if ($this->name) {
            return $this->name;
        }
        if ($this->relationLoaded('block') && $this->block) {
            return $this->block->name ?? 'Unknown Block';
        }
        return 'Unknown Block';
    }

    /**
     * Scope: Eager-load block definition with fields and source block fields
     */
    public function scopeWithBlockData(Builder $query): Builder
    {
        return $query->with(['block', 'block.fields', 'sourceBlock.block.fields']);
    }

    /**
     * Scope: Filter by entity type and ID
     */
    public function scopeForEntity(Builder $query, string $type, int $id): Builder
    {
        return $query->where('entity_type', $type)->where('entity_id', $id);
    }

    /**
     * Scope: Filter for pages
     */
    public function scopeForPage(Builder $query, int $pageId): Builder
    {
        return $query->forEntity('page', $pageId);
    }

    /**
     * Scope: Order by display order
     */
    public function scopeOrdered(Builder $query): Builder
    {
        return $query->orderBy('order');
    }

    /**
     * Scope: Filter to published blocks only
     */
    public function scopePublished(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_PUBLISHED);
    }

    /**
     * Scope: Filter to draft blocks only
     */
    public function scopeDraft(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_DRAFT);
    }

    /**
     * Check if this block is published
     */
    public function isPublished(): bool
    {
        return $this->status === self::STATUS_PUBLISHED;
    }

    /**
     * Check if this block is a draft
     */
    public function isDraft(): bool
    {
        return $this->status === self::STATUS_DRAFT;
    }

    /**
     * Publish this block
     */
    public function publish(): self
    {
        $this->status = self::STATUS_PUBLISHED;
        $this->save();
        return $this;
    }

    /**
     * Unpublish this block (set to draft)
     */
    public function unpublish(): self
    {
        $this->status = self::STATUS_DRAFT;
        $this->save();
        return $this;
    }

    /**
     * Get a specific field value from data
     */
    public function getFieldValue(string $fieldName, $default = null)
    {
        $data = $this->data ?? [];
        return $data[$fieldName] ?? $default;
    }

    /**
     * Set a specific field value in data
     */
    public function setFieldValue(string $fieldName, $value): self
    {
        $data = $this->data ?? [];
        $data[$fieldName] = $value;
        $this->data = $data;
        return $this;
    }

    /**
     * Get all field values merged with defaults from block definition
     */
    public function getFieldsWithDefaults(): array
    {
        $block = $this->relationLoaded('block') ? $this->block : $this->block()->with('fields')->first();
        if (!$block) {
            return $this->data ?? [];
        }
        if (!$block->relationLoaded('fields')) {
            $block->load('fields');
        }

        $result = [];
        foreach ($block->fields as $field) {
            $fieldName = $field->name;
            $result[$fieldName] = $this->getFieldValue($fieldName, $field->default_value);
        }

        return $result;
    }

    /**
     * Create initial data structure from block definition
     */
    public static function createInitialData(BlockRegistry $block): array
    {
        $data = [];
        foreach ($block->fields as $field) {
            $data[$field->name] = $field->default_value ?? '';
        }
        return $data;
    }

    /**
     * Get blocks for a page with block definitions loaded
     */
    public static function getBlocksForPage(int $pageId): \Illuminate\Database\Eloquent\Collection
    {
        return self::forPage($pageId)
            ->with(['block', 'block.fields', 'block.category'])
            ->ordered()
            ->get();
    }

    /**
     * Get blocks for any entity type with block definitions loaded
     */
    public static function getBlocksForEntity(string $type, int $id): \Illuminate\Database\Eloquent\Collection
    {
        return self::forEntity($type, $id)
            ->with(['block', 'block.fields', 'block.category'])
            ->ordered()
            ->get();
    }

    /**
     * Get maximum order for an entity's blocks
     */
    public static function getMaxOrder(string $type, int $id): int
    {
        return self::forEntity($type, $id)->max('order') ?? -1;
    }

    /**
     * Boot method for model events
     */
    protected static function boot(): void
    {
        parent::boot();

        static::creating(function ($entityBlock) {
            // Auto-set order if not provided
            if ($entityBlock->order === null) {
                $entityBlock->order = self::getMaxOrder(
                    $entityBlock->entity_type,
                    $entityBlock->entity_id
                ) + 1;
            }

            // Initialize data with defaults if empty (skip for global references)
            if (empty($entityBlock->data) && !$entityBlock->source_block_id) {
                $block = BlockRegistry::with('fields')->find($entityBlock->block_id);
                if ($block) {
                    $entityBlock->data = self::createInitialData($block);
                }
            }
        });

        // Clear rendered block cache when any entity block changes
        $clearCache = function ($entityBlock) {
            EntityBlockService::clearCache($entityBlock->entity_type, $entityBlock->entity_id);

            // If this is a global source block, also clear all referencing entities
            if ($entityBlock->entity_type === 'global') {
                foreach ($entityBlock->references as $ref) {
                    EntityBlockService::clearCache($ref->entity_type, $ref->entity_id);
                }
            }
        };

        static::saved($clearCache);
        static::deleted($clearCache);
    }
}
