<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Core;

use Ekesto\CacheInvalidator\ContentVisibility;

/**
 * Passive content lifecycle classifier for shadow diagnostics.
 */
final class ContentLifecycle
{
    /**
     * Classify a post lifecycle transition without affecting execution.
     *
     * @param \WP_Post|null $oldPost Previous post snapshot, when available.
     * @param \WP_Post|null $newPost Current post snapshot, when available.
     * @param array<string,mixed> $metadata Additional transition metadata.
     * @return array<string,mixed> Structured lifecycle classification.
     */
    public static function classifyTransition(?\WP_Post $oldPost, ?\WP_Post $newPost, array $metadata = []): array
    {
        $post = $newPost ?? $oldPost;
        $postId = $post instanceof \WP_Post ? (int) $post->ID : 0;
        $postType = $post instanceof \WP_Post ? sanitize_key((string) $post->post_type) : '';
        $oldStatus = $oldPost instanceof \WP_Post ? sanitize_key((string) $oldPost->post_status) : '';
        $newStatus = $newPost instanceof \WP_Post ? sanitize_key((string) $newPost->post_status) : '';
        $oldPassword = $oldPost instanceof \WP_Post ? (string) $oldPost->post_password : '';
        $newPassword = $newPost instanceof \WP_Post ? (string) $newPost->post_password : '';
        $preciseUrl = $newPost instanceof \WP_Post ? ContentVisibility::resolvePreciseUrlForPost($newPost) : '';

        $visibilityState = self::resolveVisibilityState($newStatus);
        $accessState = $newPassword !== '' ? 'password_protected' : 'public';
        if ($newStatus === 'private') {
            $accessState = 'private';
        }

        return [
            'reason' => self::resolveReason($oldStatus, $newStatus, $oldPassword, $newPassword),
            'post_id' => $postId,
            'post_type' => $postType,
            'old_status' => $oldStatus,
            'new_status' => $newStatus,
            'visibility_state' => $visibilityState,
            'access_state' => $accessState,
            'warmability' => [
                'self_url_warmable' => $preciseUrl !== '',
                'precise_url' => $preciseUrl,
            ],
            'metadata' => array_merge([
                'old_password_present' => $oldPassword !== '',
                'new_password_present' => $newPassword !== '',
                'post_password_changed' => $oldPassword !== $newPassword,
                'is_publicly_viewable' => $newPost instanceof \WP_Post && function_exists('is_post_publicly_viewable')
                    ? is_post_publicly_viewable($newPost)
                    : $newStatus === 'publish',
            ], $metadata),
        ];
    }

    /**
     * Resolve one shadow lifecycle reason.
     *
     * @param string $oldStatus Previous status.
     * @param string $newStatus Current status.
     * @param string $oldPassword Previous post password.
     * @param string $newPassword Current post password.
     * @return string Shadow lifecycle reason.
     */
    private static function resolveReason(string $oldStatus, string $newStatus, string $oldPassword, string $newPassword): string
    {
        if ($oldStatus === 'publish' && $newStatus === 'publish') {
            if ($oldPassword === '' && $newPassword !== '') {
                return 'access_restricted';
            }

            if ($oldPassword !== '' && $newPassword === '') {
                return 'access_restored';
            }

            return 'update_published';
        }

        if ($oldStatus === 'publish' && $newStatus !== 'publish') {
            return $newStatus === 'private' ? 'access_restricted' : 'visibility_hidden';
        }

        if ($oldStatus === 'private' && $newStatus === 'publish') {
            return 'access_restored';
        }

        if ($oldStatus !== 'publish' && $newStatus === 'publish') {
            return 'visibility_restored';
        }

        return 'none';
    }

    /**
     * Resolve a compact visibility state label.
     *
     * @param string $status Post status.
     * @return string Visibility state.
     */
    private static function resolveVisibilityState(string $status): string
    {
        if ($status === 'publish') {
            return 'visible';
        }

        if ($status === 'trash') {
            return 'trashed';
        }

        if ($status === 'private') {
            return 'private';
        }

        return 'hidden';
    }
}
