<?php
/**
 * Per-project configuration resolution.
 *
 * Each tilvora_visualizer post can independently have:
 *   - its own uploaded .glb model (_tilvora_model_id)
 *   - its own config.json text (_tilvora_config_json)
 *
 * Missing or empty values fall back to the bundled defaults so the
 * visualizer works immediately after creating a new project.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* ── Project availability ──────────────────────────────────────────── */

function tilvora_is_enabled_for_project( $project_id ) {
	$project_id = absint( $project_id );
	if ( ! $project_id ) {
		return false;
	}
	return TILVORA_CPT === get_post_type( $project_id )
		&& 'publish' === get_post_status( $project_id );
}

/* ── URLs ──────────────────────────────────────────────────────────── */

function tilvora_get_configurator_url( $project_id ) {
	return add_query_arg( TILVORA_QUERY_VAR, absint( $project_id ), home_url( '/' ) );
}

function tilvora_get_config_feed_url( $project_id ) {
	return add_query_arg( TILVORA_CONFIG_QUERY_VAR, absint( $project_id ), home_url( '/' ) );
}

function tilvora_get_materials_feed_url() {
	return add_query_arg( TILVORA_MATERIALS_QUERY_VAR, 1, home_url( '/' ) );
}

/* ── Default assets ────────────────────────────────────────────────── */

function tilvora_get_default_model_url() {
	return TILVORA_PLUGIN_URL . 'assets/models/floor.glb';
}

function tilvora_get_default_config_json_raw() {
	$path = TILVORA_PLUGIN_DIR . 'assets/config.json';
	if ( ! file_exists( $path ) ) {
		return '{}';
	}
	$contents = file_get_contents( $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
	return false === $contents ? '{}' : $contents;
}

/* ── Per-project model URL ─────────────────────────────────────────── */

function tilvora_get_model_url( $project_id ) {
	$project_id = absint( $project_id );
	$model_id   = absint( get_post_meta( $project_id, TILVORA_META_MODEL_ID, true ) );

	if ( $model_id ) {
		$url = wp_get_attachment_url( $model_id );
		if ( $url ) {
			return $url;
		}
	}

	return tilvora_get_default_model_url();
}

/* ── Config JSON storage — direct DB to avoid metadata filter corruption ── */

/**
 * Read config JSON directly from wp_postmeta, bypassing get_post_meta().
 * Something on this install hooks generic metadata filters and strips
 * backslash-escaped quotes (e.g. \" inside SVG icon strings). Confirmed
 * via standalone reproduction: direct $wpdb reads/writes round-trip perfectly
 * while the metadata API does not.
 */
function tilvora_db_get_config_json_raw( $project_id ) {
	global $wpdb;
	$project_id = absint( $project_id );
	$cache_key  = 'tilvora_config_json_raw_' . $project_id;
	$cached     = wp_cache_get( $cache_key, 'tilvora' );

	if ( false !== $cached ) {
		return (string) $cached;
	}

	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Deliberately bypasses get_post_meta(); see the doc comment above this function.
	$value = $wpdb->get_var(
		$wpdb->prepare(
			"SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s LIMIT 1",
			$project_id,
			TILVORA_META_CONFIG_JSON
		)
	);
	$value = (string) $value;

	wp_cache_set( $cache_key, $value, 'tilvora' );
	return $value;
}

function tilvora_db_set_config_json_raw( $project_id, $raw ) {
	global $wpdb;
	$project_id = absint( $project_id );
	$raw        = (string) $raw;

	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.SlowDBQuery.slow_db_query_meta_key,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Deliberately bypasses update_post_meta(); see the doc comment above tilvora_db_get_config_json_raw().
	$exists = $wpdb->get_var(
		$wpdb->prepare(
			"SELECT meta_id FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s LIMIT 1",
			$project_id,
			TILVORA_META_CONFIG_JSON
		)
	);

	if ( $exists ) {
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.SlowDBQuery.slow_db_query_meta_value,WordPress.DB.SlowDBQuery.slow_db_query_meta_key,PluginCheck.Security.DirectDB.UnescapedDBParameter
		$wpdb->update(
			$wpdb->postmeta,
			[ 'meta_value' => $raw ],
			[ 'post_id' => $project_id, 'meta_key' => TILVORA_META_CONFIG_JSON ]
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.SlowDBQuery.slow_db_query_meta_value,WordPress.DB.SlowDBQuery.slow_db_query_meta_key,PluginCheck.Security.DirectDB.UnescapedDBParameter
	} else {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.SlowDBQuery.slow_db_query_meta_key,WordPress.DB.SlowDBQuery.slow_db_query_meta_value,PluginCheck.Security.DirectDB.UnescapedDBParameter
		$wpdb->insert(
			$wpdb->postmeta,
			[ 'post_id' => $project_id, 'meta_key' => TILVORA_META_CONFIG_JSON, 'meta_value' => $raw ]
		);
	}

	wp_cache_set( 'tilvora_config_json_raw_' . $project_id, $raw, 'tilvora' );
}

function tilvora_get_custom_config_json_raw( $project_id ) {
	return tilvora_db_get_config_json_raw( absint( $project_id ) );
}

/* ── JSON sanitization ─────────────────────────────────────────────── */

function tilvora_config_kses_allowlist() {
	return [
		'svg'      => [ 'width' => true, 'height' => true, 'viewbox' => true, 'fill' => true, 'xmlns' => true, 'class' => true, 'aria-hidden' => true, 'role' => true ],
		'path'     => [ 'd' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-dasharray' => true, 'class' => true ],
		'rect'     => [ 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'class' => true ],
		'circle'   => [ 'cx' => true, 'cy' => true, 'r' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'class' => true ],
		'line'     => [ 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true ],
		'polyline' => [ 'points' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true ],
		'polygon'  => [ 'points' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true ],
		'g'        => [ 'fill' => true, 'stroke' => true, 'class' => true, 'transform' => true ],
	];
}

function tilvora_sanitize_config_values( $value ) {
	if ( is_array( $value ) ) {
		return array_map( 'tilvora_sanitize_config_values', $value );
	}
	if ( is_float( $value ) ) {
		return round( $value, 6 );
	}
	if ( ! is_string( $value ) ) {
		return $value;
	}
	if ( '<' === substr( ltrim( $value ), 0, 1 ) ) {
		return wp_kses( $value, tilvora_config_kses_allowlist() );
	}
	return sanitize_text_field( $value );
}

function tilvora_sanitize_config_json( $raw ) {
	$raw = trim( (string) $raw );
	if ( '' === $raw ) {
		return '';
	}
	$decoded = json_decode( $raw, true );
	if ( ! is_array( $decoded ) ) {
		return new WP_Error( 'tilvora_invalid_json', __( 'Config JSON is not valid.', 'tilvora-3d-flooring-visualizer' ) );
	}
	$sanitized = tilvora_sanitize_config_values( $decoded );
	return wp_json_encode( $sanitized, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT );
}

function tilvora_validate_config_json( $json_string ) {
	$json_string = trim( (string) $json_string );
	if ( '' === $json_string ) {
		return [];
	}
	$decoded = json_decode( $json_string, true );
	if ( null === $decoded && JSON_ERROR_NONE !== json_last_error() ) {
		return new WP_Error( 'tilvora_invalid_json', sprintf(
			/* translators: %s: JSON parser error message */
			__( 'Config JSON is not valid: %s', 'tilvora-3d-flooring-visualizer' ),
			json_last_error_msg()
		) );
	}
	if ( ! is_array( $decoded ) ) {
		return new WP_Error( 'tilvora_invalid_json', __( 'Config JSON must be a JSON object.', 'tilvora-3d-flooring-visualizer' ) );
	}
	return $decoded;
}

/* ── Resolved config (custom or bundled default, with overridden model) ── */

function tilvora_get_resolved_config_array( $project_id ) {
	$project_id = absint( $project_id );
	$custom_raw = tilvora_get_custom_config_json_raw( $project_id );

	$config = null;
	if ( '' !== trim( $custom_raw ) ) {
		$decoded = json_decode( $custom_raw, true );
		if ( is_array( $decoded ) ) {
			$config = $decoded;
		}
	}

	if ( null === $config ) {
		$decoded = json_decode( tilvora_get_default_config_json_raw(), true );
		$config  = is_array( $decoded ) ? $decoded : [];
	}

	// Only override modelPath if an attachment was explicitly uploaded via the media library.
	// Otherwise the config JSON's own modelPath is used as-is (supports preset paths like "assets/models/bathroom.glb").
	$model_id = absint( get_post_meta( $project_id, TILVORA_META_MODEL_ID, true ) );
	if ( $model_id ) {
		$model_url = wp_get_attachment_url( $model_id );
		if ( $model_url ) {
			$config['modelPath'] = $model_url;
		}
	}

	$title = get_the_title( $project_id );
	if ( $title ) {
		$config['id']          = (string) $project_id;
		$config['productName'] = $title;
	}

	return $config;
}

function tilvora_get_resolved_config_json( $project_id ) {
	return wp_json_encode( tilvora_get_resolved_config_array( $project_id ) );
}
