<?php

namespace FluxCms\Core\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Translatable\HasTranslations;

class Page extends Model implements HasMedia
{
    use HasTranslations, InteractsWithMedia;

    public function getTable()
    {
        return config('flux-cms.database.table_prefix', 'flux_cms_').'pages';
    }

    protected $fillable = [
        'domain_key',
        'title',
        'meta_description',
        'meta_keywords',
        'og_image',
        'canonical_url',
        'is_published',
        'published_at',
        'settings',
    ];

    protected $translatable = [
        'title',
        'meta_description',
        'meta_keywords',
        'og_image',
    ];

    protected $casts = [
        'title' => 'array',
        'meta_description' => 'array',
        'meta_keywords' => 'array',
        'og_image' => 'array',
        'settings' => 'array',
        'is_published' => 'boolean',
        'published_at' => 'datetime',
    ];

    public function components(): HasMany
    {
        return $this->hasMany(PageComponent::class, 'page_id')
            ->where('is_active', true)
            ->orderBy('order');
    }

    public function allComponents(): HasMany
    {
        return $this->hasMany(PageComponent::class, 'page_id')
            ->orderBy('order');
    }

    public function versions(): HasMany
    {
        return $this->hasMany(PageVersion::class, 'page_id')
            ->orderBy('created_at', 'desc');
    }

    public function navigationItems(): HasMany
    {
        return $this->hasMany(NavigationItem::class, 'page_id');
    }

    /**
     * Media Collections für SEO Images
     */
    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('og_images')
            ->singleFile()
            ->acceptsMimeTypes(['image/jpeg', 'image/png', 'image/webp']);

        $this->addMediaCollection('page_assets')
            ->acceptsMimeTypes(['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml']);
    }

    public function registerMediaConversions(?Media $media = null): void
    {
        $this->addMediaConversion('og_thumb')
            ->width(1200)
            ->height(630)
            ->sharpen(10);

        $this->addMediaConversion('preview')
            ->width(400)
            ->height(300)
            ->sharpen(10);
    }

    /**
     * Scopes
     */
    public function scopeForDomain($query, string $domainKey)
    {
        return $query->where('domain_key', $domainKey);
    }

    public function scopePublished($query)
    {
        return $query->where('is_published', true)
            ->where(function ($q) {
                $q->whereNull('published_at')
                    ->orWhere('published_at', '<=', now());
            });
    }

    public function scopeBySlug($query, string $slug, ?string $locale = null)
    {
        $locale = $locale ?? app()->getLocale();

        return $query->whereHas('slugs', function ($q) use ($slug, $locale) {
            $q->where('slug', $slug)->where('locale', $locale);
        });
    }

    public function scopeBySlugWithFallback($query, string $slug, ?string $locale = null)
    {
        $locale = $locale ?? app()->getLocale();
        $fallbackLocale = config('app.fallback_locale');

        return $query->where(function ($q) use ($slug, $locale, $fallbackLocale) {
            $q->whereHas('slugs', function ($sq) use ($slug, $locale) {
                $sq->where('slug', $slug)->where('locale', $locale);
            })->orWhereHas('slugs', function ($sq) use ($slug, $fallbackLocale) {
                $sq->where('slug', $slug)->where('locale', $fallbackLocale);
            });
        });
    }

    /**
     * Slug relationship
     */
    public function slugs()
    {
        return $this->morphMany(Slug::class, 'model');
    }

    /**
     * Version Management
     */
    public function createVersion(?string $changeDescription = null, ?Model $user = null): PageVersion
    {
        $version = $this->versions()->make([
            'page_data' => $this->toArray(),
            'components_data' => $this->allComponents()->with('media')->get()->toArray(),
            'change_description' => $changeDescription,
            'version_name' => $this->generateVersionName(),
        ]);

        if ($user) {
            $version->createdBy()->associate($user);
        }

        $version->save();

        return $version;
    }

    protected function generateVersionName(): string
    {
        $count = $this->versions()->count();

        return 'Version '.($count + 1).' - '.now()->format('Y-m-d H:i');
    }

    /**
     * Publishing Methods
     */
    public function publish(): void
    {
        $this->update([
            'is_published' => true,
            'published_at' => $this->published_at ?? now(),
        ]);
    }

    public function unpublish(): void
    {
        $this->update(['is_published' => false]);
    }

    /**
     * SEO Methods
     */
    public function getSeoTitle(?string $locale = null): string
    {
        $locale = $locale ?? app()->getLocale();
        $title = $this->getTranslation('title', $locale);
        $siteName = config('flux-cms.seo.site_name', config('app.name'));

        return $title ? "{$title} - {$siteName}" : $siteName;
    }

    public function getSeoDescription(?string $locale = null): string
    {
        $locale = $locale ?? app()->getLocale();

        return $this->getTranslation('meta_description', $locale) ?? '';
    }

    public function getCanonicalUrl(): string
    {
        return $this->canonical_url ?: request()->url();
    }

    /**
     * URL Generation
     */
    public function getUrl(?string $locale = null): string
    {
        $locale = $locale ?? app()->getLocale();
        $slug = $this->slugs()->where('locale', $locale)->first();

        if (! $slug || $slug->slug === '/') {
            return url('/');
        }

        return url(ltrim($slug->slug, '/'));
    }

    /**
     * Settings Management
     */
    public function getSetting(string $key, mixed $default = null): mixed
    {
        return data_get($this->settings, $key, $default);
    }

    public function setSetting(string $key, mixed $value): void
    {
        $settings = $this->settings ?? [];
        data_set($settings, $key, $value);
        $this->update(['settings' => $settings]);
    }
}
