<?php

declare(strict_types=1);

namespace Builder\Core\Foundation\Concerns;

use Builder\Core\Foundation\Contracts\Translatable;
use Builder\Core\Foundation\Enums\PublicationStatus;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;

/**
 * Translatable publishing — every translation row carries its own
 * publishing status, so different locales can be drafts/published
 * independently.
 *
 * The consuming model must:
 *   – `use HasTranslations` (this trait calls `getTranslation()` and
 *     accesses the `$translations` relation).
 *   – have its translation rows `use HasPublishing` (this trait calls
 *     `publish()` / `unpublish()` / etc. on each translation).
 *
 * @property Collection<int, Model> $translations
 *
 * @phpstan-require-extends Model
 *
 * @phpstan-require-implements Translatable
 */
trait HasTranslatablePublishing
{
    public function publish(?string $locale = null): void
    {
        if ($locale !== null) {
            $translation = $this->getTranslation($locale);
            $this->callIfMethod($translation, 'publish');

            return;
        }

        foreach ($this->translations as $translation) {
            $this->callIfMethod($translation, 'publish');
        }
    }

    public function unpublish(?string $locale = null): void
    {
        if ($locale !== null) {
            $translation = $this->getTranslation($locale);
            $this->callIfMethod($translation, 'unpublish');

            return;
        }

        foreach ($this->translations as $translation) {
            $this->callIfMethod($translation, 'unpublish');
        }
    }

    public function schedulePublish(Carbon $at, ?string $locale = null): void
    {
        if ($locale !== null) {
            $translation = $this->getTranslation($locale);
            $this->callIfMethod($translation, 'schedulePublish', $at);

            return;
        }

        foreach ($this->translations as $translation) {
            $this->callIfMethod($translation, 'schedulePublish', $at);
        }
    }

    public function isPublished(?string $locale = null): bool
    {
        return $this->translationBoolean($locale, 'isPublished');
    }

    public function isDraft(?string $locale = null): bool
    {
        return $this->translationBoolean($locale, 'isDraft', true);
    }

    public function isScheduled(?string $locale = null): bool
    {
        return $this->translationBoolean($locale, 'isScheduled');
    }

    /**
     * Virtual attribute exposing the current locale's translation status string.
     * Allows Column::publishingStatus() to work transparently on the index table.
     * Requires 'translations' to be eager-loaded via the resource's eagerLoad() method.
     */
    public function getStatusAttribute(): ?string
    {
        $current = $this->translations
            ->where('locale', app()->getLocale())
            ->first();

        $status = $current?->getAttribute('status');

        if ($status instanceof PublicationStatus) {
            return $status->value;
        }

        return is_string($status) ? $status : null;
    }

    private function callIfMethod(?Model $target, string $method, mixed ...$arguments): void
    {
        if ($target !== null && method_exists($target, $method)) {
            $target->{$method}(...$arguments);
        }
    }

    private function translationBoolean(?string $locale, string $method, bool $defaultWhenMissing = false): bool
    {
        $resolveLocale = $locale ?? app()->getLocale();
        $translation = $this->getTranslation($resolveLocale);

        if ($translation === null || ! method_exists($translation, $method)) {
            return $defaultWhenMissing;
        }

        return (bool) $translation->{$method}();
    }
}
