<?php
/**
 * Item detail payloads for view modals (values, tables, ownership).
 *
 * @package DatabaseCleanup
 */

namespace DatabaseCleanup\Includes;

use DatabaseCleanup\Module;

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

class Item_Detail {

	/**
	 * @param array<string, mixed> $context
	 * @return array<string, mixed>
	 */
	public static function get( string $type, string $id, array $context = [] ): array {
		$type = sanitize_key( $type );

		return match ( $type ) {
			'table'       => self::table_detail( $id ),
			'option'      => self::option_detail( $id ),
			'transient'   => self::transient_detail( $id ),
			'post_meta', 'user_meta', 'comment_meta', 'term_meta' => self::meta_detail( $type, $id ),
			'cron'        => self::cron_detail( $id, $context ),
			'post_type'   => self::post_type_detail( $id, $context ),
			default       => [ 'error' => __( 'Unsupported item type.', 'wp-powersuite' ) ],
		};
	}

	/**
	 * @return array<string, mixed>
	 */
	private static function table_detail( string $table ): array {
		global $wpdb;

		if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $table ) ) {
			return [ 'error' => __( 'Invalid table name.', 'wp-powersuite' ) ];
		}

		$db_name   = defined( 'DB_NAME' ) ? DB_NAME : '';
		$ownership = Item_Ownership::resolve( 'table', $table );
		$safe      = '`' . esc_sql( $table ) . '`';

		if ( $db_name !== '' ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			$status = $wpdb->get_row( "SHOW TABLE STATUS FROM `" . esc_sql( $db_name ) . "` LIKE '" . esc_sql( $table ) . "'", ARRAY_A );
		} else {
			$status = null;
		}
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$columns = $wpdb->get_results( "SHOW FULL COLUMNS FROM {$safe}", ARRAY_A );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$indexes = $wpdb->get_results( "SHOW INDEX FROM {$safe}", ARRAY_A );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$create = $wpdb->get_row( "SHOW CREATE TABLE {$safe}", ARRAY_N );

		return [
			'title'     => $table,
			'ownership' => $ownership,
			'status'    => is_array( $status ) ? $status : [],
			'columns'   => is_array( $columns ) ? $columns : [],
			'indexes'   => is_array( $indexes ) ? $indexes : [],
			'create_sql'=> is_array( $create ) ? (string) ( $create[1] ?? '' ) : '',
		];
	}

	/**
	 * @return array<string, mixed>
	 */
	private static function option_detail( string $id ): array {
		global $wpdb;

		if ( is_numeric( $id ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$row = $wpdb->get_row( $wpdb->prepare( "SELECT option_name, option_value, autoload FROM {$wpdb->options} WHERE option_id = %d", (int) $id ), ARRAY_A );
		} else {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$row = $wpdb->get_row( $wpdb->prepare( "SELECT option_name, option_value, autoload FROM {$wpdb->options} WHERE option_name = %s", $id ), ARRAY_A );
		}

		if ( ! is_array( $row ) ) {
			return [ 'error' => __( 'Option not found.', 'wp-powersuite' ) ];
		}

		$name = (string) ( $row['option_name'] ?? '' );
		$value = (string) ( $row['option_value'] ?? '' );

		return [
			'title'     => $name,
			'ownership' => Item_Ownership::resolve( 'option', $name ),
			'autoload'  => (string) ( $row['autoload'] ?? '' ),
			'size'      => Module::formatBytes( strlen( $value ) ),
			'value'     => self::format_value( $value ),
		];
	}

	/**
	 * @return array<string, mixed>
	 */
	private static function transient_detail( string $name ): array {
		$value = get_option( '_transient_' . $name );
		if ( $value === false ) {
			$value = get_option( '_site_transient_' . $name );
		}
		if ( $value === false ) {
			return [ 'error' => __( 'Transient not found.', 'wp-powersuite' ) ];
		}

		$raw = is_scalar( $value ) ? (string) $value : wp_json_encode( $value );

		return [
			'title'     => $name,
			'ownership' => Item_Ownership::resolve( 'transient', $name ),
			'value'     => self::format_value( is_string( $raw ) ? $raw : '' ),
		];
	}

	/**
	 * @return array<string, mixed>
	 */
	private static function meta_detail( string $meta_type, string $meta_id ): array {
		global $wpdb;

		$table = match ( $meta_type ) {
			'user_meta'    => $wpdb->usermeta,
			'comment_meta' => $wpdb->commentmeta,
			'term_meta'    => $wpdb->termmeta,
			default        => $wpdb->postmeta,
		};

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$table} WHERE meta_id = %d", (int) $meta_id ), ARRAY_A );
		if ( ! is_array( $row ) ) {
			return [ 'error' => __( 'Meta row not found.', 'wp-powersuite' ) ];
		}

		$key = (string) ( $row['meta_key'] ?? '' );
		$value = (string) ( $row['meta_value'] ?? '' );

		return [
			'title'     => $key,
			'ownership' => Item_Ownership::resolve( $meta_type, $meta_id, [ 'meta_key' => $key ] ),
			'size'      => Module::formatBytes( strlen( $value ) + strlen( $key ) ),
			'value'     => self::format_value( $value ),
		];
	}

	/**
	 * @param array<string, mixed> $context
	 * @return array<string, mixed>
	 */
	private static function cron_detail( string $encoded, array $context ): array {
		unset( $encoded );
		$hook = (string) ( $context['hook'] ?? '' );
		$args = is_array( $context['args'] ?? null ) ? $context['args'] : [];
		$raw  = wp_json_encode( $args ) ?: '[]';

		return [
			'title'     => $hook,
			'ownership' => Item_Ownership::resolve( 'cron', $hook, [ 'hook' => $hook ] ),
			'value'     => self::format_value( $raw ),
			'callbacks' => Item_Ownership::cron_hook_callbacks( $hook ),
		];
	}

	/**
	 * @param array<string, mixed> $context
	 * @return array<string, mixed>
	 */
	private static function post_type_detail( string $post_type, array $context ): array {
		$page     = max( 1, absint( $context['page'] ?? 1 ) );
		$per_page = Entity_Browser::sanitize_per_page( (int) ( $context['per_page'] ?? 20 ) );

		$query = new \WP_Query(
			[
				'post_type'              => sanitize_key( $post_type ),
				'post_status'            => 'any',
				'posts_per_page'         => $per_page,
				'paged'                  => $page,
				'orderby'                => 'ID',
				'order'                  => 'DESC',
				'fields'                 => 'ids',
				'no_found_rows'          => false,
				'update_post_meta_cache' => false,
				'update_post_term_cache' => false,
			]
		);

		$posts = [];
		foreach ( $query->posts as $post_id ) {
			$posts[] = [
				'id'     => (int) $post_id,
				'title'  => get_the_title( (int) $post_id ) ?: '#' . $post_id,
				'status' => get_post_status( (int) $post_id ) ?: '',
				'date'   => get_post_field( 'post_date', (int) $post_id ) ?: '',
			];
		}

		return [
			'title'       => $post_type,
			'ownership'   => Item_Ownership::resolve( 'post_type', $post_type ),
			'posts'       => $posts,
			'total'       => (int) $query->found_posts,
			'page'        => $page,
			'per_page'    => $per_page,
			'total_pages' => max( 1, (int) $query->max_num_pages ),
		];
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function format_value( string $value ): array {
		$original = $value;
		$formatted = $original;
		$format = 'text';

		if ( is_serialized( $value ) ) {
			$decoded = @unserialize( $value, [ 'allowed_classes' => false ] );
			if ( $decoded !== false || $value === 'b:0;' ) {
				$formatted = wp_json_encode( $decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ) ?: $original;
				$format = 'json';
			}
		} elseif ( self::looks_like_json( $value ) ) {
			$decoded = json_decode( $value, true );
			if ( json_last_error() === JSON_ERROR_NONE ) {
				$formatted = wp_json_encode( $decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ) ?: $original;
				$format = 'json';
			}
		}

		if ( strlen( $formatted ) > 50000 ) {
			$formatted = substr( $formatted, 0, 50000 ) . "\n…";
		}

		return [
			'original'  => $original,
			'formatted' => $formatted,
			'format'    => $format,
		];
	}

	private static function looks_like_json( string $value ): bool {
		$value = trim( $value );

		return $value !== '' && ( ( str_starts_with( $value, '{' ) && str_ends_with( $value, '}' ) ) || ( str_starts_with( $value, '[' ) && str_ends_with( $value, ']' ) ) );
	}
}
