<?php
/**
 * Shared Schema.org type registry.
 *
 * Keeps Smart Builder card routing, field-config fallback, and JSON generation
 * fallback in one place so AI-generated subtypes do not drift between PHP and JS.
 *
 * @package CustomSchemaSchemafy
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit( 'Direct access not allowed!' );
}

class SCHEMAFY_Schema_Type_Registry {

    /**
     * Cached registry payload.
     *
     * @var array|null
     */
    private static $registry = null;

    /**
     * Cached UI/card map.
     *
     * @var array|null
     */
    private static $type_map = null;

    /**
     * Cached field config fallback map.
     *
     * @var array|null
     */
    private static $fallback_map = null;

    /**
     * Cached schema generation fallback map.
     *
     * @var array|null
     */
    private static $build_map = null;

    /**
     * Return normalized registry data.
     *
     * @return array
     */
    public static function get_registry() {
        if ( self::$registry !== null ) {
            return self::$registry;
        }

        $defaults = array(
            'version'        => 1,
            'hybridFallback' => 'WebPage',
            'builderCards'   => array(
                'Article',
                'Product',
                'Organization',
                'LocalBusiness',
                'Person',
                'Event',
                'Recipe',
                'Review',
                'FAQ',
                'WebPage',
                'Course',
                'HowTo',
                'JobPosting',
                'Service',
                'Video',
                'BreadcrumbList',
            ),
            'aliases'        => array(
                'FAQPage'     => 'FAQ',
                'VideoObject' => 'Video',
            ),
            'families'       => array(),
        );

        $registry_file = SCHEMAFY_PLUGIN_PATH . 'templates/builder/schema-type-registry.json';
        $registry      = $defaults;

        if ( file_exists( $registry_file ) ) {
            $json    = file_get_contents( $registry_file );
            $decoded = json_decode( $json, true );

            if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
                $registry = array_merge( $defaults, $decoded );
            } else {
                schemafy_log( '[SCHEMAFY] Invalid schema type registry JSON: ' . $registry_file );
            }
        }

        $registry['builderCards']   = self::normalize_type_list( $registry['builderCards'] );
        $registry['aliases']        = self::normalize_type_map( $registry['aliases'] );
        $registry['families']       = self::normalize_families( $registry['families'] );
        $registry['hybridFallback'] = self::normalize_type( $registry['hybridFallback'] ) ?: 'WebPage';

        self::$registry = $registry;

        return self::$registry;
    }

    /**
     * Return the card routing map used by Smart Builder.
     *
     * @return array
     */
    public static function get_type_map() {
        if ( self::$type_map !== null ) {
            return self::$type_map;
        }

        $registry = self::get_registry();
        $map      = $registry['aliases'];

        foreach ( $registry['families'] as $parent => $children ) {
            foreach ( $children as $child ) {
                $map[ $child ] = $parent;
            }
        }

        self::$type_map = $map;

        return self::$type_map;
    }

    /**
     * Return the field configuration fallback map.
     *
     * @return array
     */
    public static function get_fallback_map() {
        if ( self::$fallback_map !== null ) {
            return self::$fallback_map;
        }

        self::$fallback_map = self::get_type_map();

        return self::$fallback_map;
    }

    /**
     * Return the builder routine fallback map.
     *
     * UI aliases are not always builder routines. Example: VideoObject opens the
     * Video card, but its schema routine is VideoObject.
     *
     * @return array
     */
    public static function get_build_map() {
        if ( self::$build_map !== null ) {
            return self::$build_map;
        }

        $map = self::get_type_map();

        $map['VideoObject'] = 'VideoObject';

        self::$build_map = $map;

        return self::$build_map;
    }

    /**
     * Return Smart Builder card types.
     *
     * @return array
     */
    public static function get_builder_cards() {
        $registry = self::get_registry();

        return $registry['builderCards'];
    }

    /**
     * Return a browser-safe registry payload.
     *
     * @return array
     */
    public static function get_client_registry() {
        $registry = self::get_registry();

        return array(
            'version'            => $registry['version'],
            'hybridFallback'     => $registry['hybridFallback'],
            'builderCards'       => $registry['builderCards'],
            'smartBuilderCards'  => $registry['builderCards'],
            'aliases'            => $registry['aliases'],
            'families'           => $registry['families'],
            'typeMap'            => self::get_type_map(),
            'configFallbackMap'  => self::get_fallback_map(),
            'buildMap'           => self::get_build_map(),
        );
    }

    /**
     * Normalize a Schema.org type string.
     *
     * @param mixed $type Type value.
     * @return string
     */
    private static function normalize_type( $type ) {
        if ( ! is_scalar( $type ) ) {
            return '';
        }

        $type = trim( (string) $type );
        $type = preg_replace( '/^https?:\/\/schema\.org\//i', '', $type );

        return $type;
    }

    /**
     * Normalize a list of Schema.org type strings.
     *
     * @param mixed $types Type list.
     * @return array
     */
    private static function normalize_type_list( $types ) {
        if ( ! is_array( $types ) ) {
            return array();
        }

        $normalized = array();

        foreach ( $types as $type ) {
            $type = self::normalize_type( $type );
            if ( $type !== '' && ! in_array( $type, $normalized, true ) ) {
                $normalized[] = $type;
            }
        }

        return $normalized;
    }

    /**
     * Normalize an associative type map.
     *
     * @param mixed $map Type map.
     * @return array
     */
    private static function normalize_type_map( $map ) {
        if ( ! is_array( $map ) ) {
            return array();
        }

        $normalized = array();

        foreach ( $map as $type => $target ) {
            $type   = self::normalize_type( $type );
            $target = self::normalize_type( $target );

            if ( $type !== '' && $target !== '' ) {
                $normalized[ $type ] = $target;
            }
        }

        return $normalized;
    }

    /**
     * Normalize the parent-to-children family registry.
     *
     * @param mixed $families Family map.
     * @return array
     */
    private static function normalize_families( $families ) {
        if ( ! is_array( $families ) ) {
            return array();
        }

        $normalized = array();

        foreach ( $families as $parent => $children ) {
            $parent = self::normalize_type( $parent );

            if ( $parent === '' ) {
                continue;
            }

            $children = self::normalize_type_list( $children );
            if ( ! empty( $children ) ) {
                $normalized[ $parent ] = $children;
            }
        }

        return $normalized;
    }
}
