<?php
/**
 * Manifest: reads sidecar .meta.json files in plugin-updates/ and returns the latest manifest for a slug.
 */

defined( 'ABSPATH' ) || exit;

class BW_Update_Server_Manifest {

	const CACHE_GROUP = 'bw_update_server';
	const CACHE_TTL   = 300; // 5 minutes

	/**
	 * Get the latest manifest for a plugin slug.
	 *
	 * @param string $slug Plugin slug (e.g. "bw-source-capture").
	 * @return array|WP_Error Manifest array or WP_Error if not found.
	 */
	public static function latest( $slug ) {
		$slug = sanitize_key( $slug );
		if ( empty( $slug ) || ! preg_match( '/^bw-[a-z0-9-]+$/', $slug ) ) {
			return new WP_Error( 'invalid_slug', 'Invalid plugin slug', array( 'status' => 400 ) );
		}

		$cached = wp_cache_get( $slug, self::CACHE_GROUP );
		if ( false !== $cached ) {
			return $cached;
		}

		$meta_file = self::find_latest_meta( $slug );
		if ( is_wp_error( $meta_file ) ) {
			return $meta_file;
		}

		$raw = file_get_contents( $meta_file );
		if ( false === $raw ) {
			return new WP_Error( 'read_failed', 'Could not read manifest file', array( 'status' => 500 ) );
		}

		$meta = json_decode( $raw, true );
		if ( ! is_array( $meta ) ) {
			return new WP_Error( 'invalid_manifest', 'Manifest file is not valid JSON', array( 'status' => 500 ) );
		}

		wp_cache_set( $slug, $meta, self::CACHE_GROUP, self::CACHE_TTL );
		return $meta;
	}

	/**
	 * Find the highest-version .meta.json file for a slug.
	 *
	 * File naming convention: bw-<slug>-<version>.meta.json
	 */
	private static function find_latest_meta( $slug ) {
		$dir = BW_UPDATE_SERVER_UPDATES_DIR;
		if ( ! is_dir( $dir ) ) {
			return new WP_Error( 'no_updates_dir', 'Updates directory not found', array( 'status' => 500 ) );
		}

		$pattern = $dir . '/' . $slug . '-*.meta.json';
		$files   = glob( $pattern );
		if ( empty( $files ) ) {
			return new WP_Error( 'not_found', sprintf( 'No manifest found for %s', $slug ), array( 'status' => 404 ) );
		}

		// Sort by version parsed from filename.
		usort(
			$files,
			function ( $a, $b ) use ( $slug ) {
				$va = self::parse_version( $a, $slug );
				$vb = self::parse_version( $b, $slug );
				return version_compare( $vb, $va );
			}
		);

		return $files[0];
	}

	private static function parse_version( $path, $slug ) {
		$basename = basename( $path, '.meta.json' );
		$prefix   = $slug . '-';
		if ( 0 === strpos( $basename, $prefix ) ) {
			return substr( $basename, strlen( $prefix ) );
		}
		return '0.0.0';
	}

	/**
	 * List all plugin slugs that have at least one manifest on disk.
	 */
	public static function list_slugs() {
		$dir = BW_UPDATE_SERVER_UPDATES_DIR;
		if ( ! is_dir( $dir ) ) {
			return array();
		}
		$files = glob( $dir . '/bw-*.meta.json' );
		$slugs = array();
		foreach ( $files as $f ) {
			$basename = basename( $f, '.meta.json' );
			// Strip trailing -X.Y.Z version
			if ( preg_match( '/^(bw-[a-z0-9-]+?)-\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.\-]+)?$/', $basename, $m ) ) {
				$slugs[ $m[1] ] = true;
			}
		}
		return array_keys( $slugs );
	}
}
