<?php

/**
 * Topic-framework seam for SocialPromoter.
 *
 * The ONE place this plugin touches the shared topic framework. Everything
 * topic-shaped - the pack, labels, per-post topics and the repost scorer's
 * topic-balance boost - goes through the bundled PublisherKits Core Library's
 * PKCORE_Classify contract (PK-CORE-SPEC-00 section 6). The core's provider
 * negotiation decides HOW posts are classified: the deterministic keyword
 * engine is the always-available floor, and when the full Classifier plugin
 * is active it answers the same calls semantically at a higher tier - this
 * plugin cannot tell and must not care.
 *
 * Promoted posts are SITE content under the core's two-universe split, so
 * this seam uses the stored-classification calls (ensure_classified /
 * topics_for_objects): the core classifies once, stores the rows, and every
 * PublisherKits plugin reads the same answer.
 *
 * @package PublisherKits_SocialPromoter
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
/**
 * Class PKITS_SP_Topics
 */
class PKITS_SP_Topics {
    /**
     * Per-request pack cache (active topics view).
     *
     * @var array|null
     */
    private static $pack = null;

    /**
     * Per-request cache of the recent-output core-topic share map.
     *
     * @var array|null
     */
    private static $recent_share = null;

    /**
     * Whether the shared topic framework is present. The core is bundled, so
     * this is true in practice; the guard keeps every caller safe if the
     * plugin's files are ever loaded without the bundle.
     *
     * @return bool
     */
    public static function available() {
        return class_exists( 'PKCORE_Classify' );
    }

    /**
     * The site pack (active topics), cached for the request.
     *
     * @return array { revision, generated_at, topics, content_types, aliases }
     */
    public static function pack() {
        if ( null === self::$pack ) {
            self::$pack = ( self::available() ? PKCORE_Classify::get_pack() : array(
                'revision'      => 0,
                'generated_at'  => '',
                'topics'        => array(),
                'content_types' => array(),
                'aliases'       => array(),
            ) );
        }
        return self::$pack;
    }

    /**
     * Whether the pack has any active topics. Before the site's topic
     * framework is set up (Site Profile > Topics), topic features quietly
     * stand down - nothing is blocked.
     *
     * @return bool
     */
    public static function has_topics() {
        $pack = self::pack();
        return !empty( $pack['topics'] );
    }

    /**
     * Human label for a topic slug, falling back to the slug itself.
     *
     * @param string $slug Topic slug.
     * @return string
     */
    public static function label( $slug ) {
        $slug = (string) $slug;
        $pack = self::pack();
        foreach ( $pack['topics'] as $t ) {
            if ( isset( $t['slug'] ) && $t['slug'] === $slug ) {
                return ( !empty( $t['label'] ) ? (string) $t['label'] : $slug );
            }
        }
        return $slug;
    }

    /**
     * The core-ring topic slugs (the only analysis axis; related and
     * peripheral rings are holding buckets, not axes).
     *
     * @return array string[]
     */
    public static function core_ring_slugs() {
        $slugs = array();
        $pack = self::pack();
        foreach ( $pack['topics'] as $t ) {
            if ( !empty( $t['slug'] ) && (empty( $t['ring'] ) || 'core' === $t['ring']) ) {
                $slugs[] = (string) $t['slug'];
            }
        }
        return $slugs;
    }

    /**
     * Stored topics for site posts, classifying-and-storing any post the core
     * has not seen (or whose classification predates the current pack).
     *
     * @param  array $post_ids Post IDs.
     * @return array Map post_id => string[] topic slugs ([] when unclassified).
     */
    public static function topics_for_posts( array $post_ids ) {
        $post_ids = array_values( array_unique( array_map( 'intval', $post_ids ) ) );
        if ( empty( $post_ids ) || !self::available() || !self::has_topics() ) {
            return array();
        }
        $rows = PKCORE_Classify::ensure_classified( $post_ids );
        $map = array();
        foreach ( $rows as $id => $row ) {
            $map[(int) $id] = ( !empty( $row['topics'] ) && is_array( $row['topics'] ) ? array_map( 'strval', $row['topics'] ) : array() );
        }
        return $map;
    }

}
