<?php

namespace App\Models;

use App\Events\SitemapDataChanged;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;

class Article extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'id',
        'type',
        'title',
        'slug',
        'image',
        'description',
        'content',
        'excerpt',
        'primary_category_id',
        'focus_keyword',
        'robots',
        'canonical_url',
        'og_image',
        'seo_score',
        'views',
        'view_time',
        'preview_key',
        'status',
        'user_id',
        'wp_id',
        'sync_status',
        'sync_at',
        'created_at',
        'updated_at',
        'deleted_at',
    ];

    protected $casts = [
        /* 'slug'                => \App\Casts\UrlDecodeCast::class, */
        'type'                => 'integer',
        'views'               => 'integer',
        'view_time'           => 'integer',
        'primary_category_id' => 'integer',
        'seo_score'           => 'integer',
        'user_id'             => 'integer',
        'wp_id'               => 'integer',
        'sync_status'         => 'boolean',
    ];

    // Relationship with User (author)
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    // Relationship with Category (primary_category_id)
    public function primaryCategory(): BelongsTo
    {
        return $this->belongsTo(Category::class, 'primary_category_id')->with('parent');
    }

    // Many-to-many relationship with categories
    public function categories(): BelongsToMany
    {
        return $this->belongsToMany(Category::class, 'article_categories')
            ->withTimestamps()
            ->wherePivotNull('deleted_at');
    }

    // Many-to-many relationship with tags
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class, 'article_tags')
            ->withTimestamps()
            ->wherePivotNull('deleted_at');
    }

    // One-to-many relationship with article views
    public function articleViews(): HasMany
    {
        return $this->hasMany(ArticleView::class);
    }

    // Scopes
    public function scopePublished($query)
    {
        return $query->where('status', 'publish');
    }

    public function scopePrivate($query)
    {
        return $query->where('status', 'private');
    }

    public function scopeDraft($query)
    {
        return $query->where('status', 'draft');
    }

    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    public function scopeFuture($query)
    {
        return $query->where('status', 'future');
    }

    // Generate slug from title
    public static function generateSlug(string $title, ?int $excludeId = null): string
    {
        $slug = Str::slug($title);
        $originalSlug = $slug;
        $counter = 1;

        $query = static::where('slug', $slug);
        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }

        while ($query->exists()) {
            $slug = $originalSlug . '-' . $counter;
            $counter++;
            $query = static::where('slug', $slug);
            if ($excludeId) {
                $query->where('id', '!=', $excludeId);
            }
        }

        return $slug;
    }

    // Calculate reading time based on Japanese text
    public static function calculateReadingTime(string $content): int
    {
        // Remove HTML tags
        $plainText = strip_tags($content);

        // Count Japanese characters (hiragana, katakana, kanji)
        $japaneseCharCount = preg_match_all('/[\x{3040}-\x{309F}\x{30A0}-\x{30FF}\x{4E00}-\x{9FAF}]/u', $plainText);

        // Count other characters
        $otherCharCount = mb_strlen($plainText) - $japaneseCharCount;

        // Japanese reading speed: approximately 400-600 characters per minute
        // Other languages: approximately 200-250 words per minute (5 characters per word)
        $japaneseReadingTime = ceil($japaneseCharCount / 500); // characters per minute
        $otherReadingTime = ceil($otherCharCount / 1000); // roughly 200 words * 5 chars per word

        $totalTime = $japaneseReadingTime + $otherReadingTime;

        return max(1, $totalTime); // minimum 1 minute
    }

    // Generate preview key
    public static function generatePreviewKey(): string
    {
        return Str::random(32);
    }

    // Get preview URL for article
    public function getPreviewUrl(): string
    {
        if ($this->type === 0) {
            // Mattock blog type (blog.detail route)
            $categorySlug = $this->primaryCategory->slug ?? 'detail';

            return route('blog.detail', [
                'category' => $categorySlug,
                'slug'     => $this->slug,
                'preview'  => $this->preview_key,
            ]);
        } else {
            // Pipopa blog type (pipopa.blog.detail route)
            return route('pipopa.blog.detail', [
                'slug'    => $this->slug,
                'preview' => $this->preview_key,
            ]);
        }
    }

    // Check if article can be previewed (has preview key)
    public function canPreview(): bool
    {
        return ! empty($this->preview_key);
    }

    // Check if article is published
    public function isPublished(): bool
    {
        return $this->status === 'publish';
    }

    // Track article view with IP-based session limit
    public function trackView(string $ipAddress): bool
    {
        // Check if this IP has viewed this article in the last 15 minutes
        $recentView = $this->articleViews()
            ->where('ip_address', $ipAddress)
            ->where('viewed_at', '>=', Carbon::now()->subMinutes(15))
            ->exists();

        if (! $recentView) {
            // Create new view record
            $this->articleViews()->create([
                'ip_address' => $ipAddress,
                'viewed_at'  => Carbon::now(),
            ]);

            // Increment the views counter
            $this->increment('views');

            return true;
        }

        return false;
    }

    // Override delete to handle soft delete of related data
    protected static function boot()
    {
        parent::boot();

        static::deleting(function ($article) {
            // Soft delete related pivot records
            $article->categories()->wherePivotNull('deleted_at')->updateExistingPivot(
                $article->categories()->pluck('categories.id'),
                ['deleted_at' => now()]
            );

            $article->tags()->wherePivotNull('deleted_at')->updateExistingPivot(
                $article->tags()->pluck('tags.id'),
                ['deleted_at' => now()]
            );
        });

        static::restoring(function ($article) {
            // Restore related pivot records
            $article->categories()->wherePivotNotNull('deleted_at')->updateExistingPivot(
                $article->categories()->withTrashed()->pluck('categories.id'),
                ['deleted_at' => null]
            );

            $article->tags()->wherePivotNotNull('deleted_at')->updateExistingPivot(
                $article->tags()->withTrashed()->pluck('tags.id'),
                ['deleted_at' => null]
            );
        });

        static::creating(function ($article) {
            if (empty($article->slug)) {
                $article->slug = static::generateSlug($article->title);
            }
            if (empty($article->preview_key)) {
                $article->preview_key = static::generatePreviewKey();
            }
            if (empty($article->view_time)) {
                $article->view_time = static::calculateReadingTime($article->content);
            }
        });

        static::updating(function ($article) {
            if ($article->isDirty('title') && empty($article->slug)) {
                $article->slug = static::generateSlug($article->title, $article->id);
            }
            if ($article->isDirty('content')) {
                $article->view_time = static::calculateReadingTime($article->content);
            }
        });

        // Fire sitemap data changed event when article is created, updated, or deleted
        static::created(function ($article) {
            event(new SitemapDataChanged);
        });

        static::updated(function ($article) {
            event(new SitemapDataChanged);
        });

        static::deleted(function ($article) {
            event(new SitemapDataChanged);
        });

        static::restored(function ($article) {
            event(new SitemapDataChanged);
        });
    }
}
