<?php
/**
 * Write CSS variable files to uploads/joc-blockora-morph.
 *
 * @package JOCBM
 *
 * Copyright (C) 2026 JayOnCode
 * License: GPL-2.0-or-later
 */

namespace JOCBM\Services;

defined( 'ABSPATH' ) || exit;

/**
 * Class CssWriter
 */
final class CssWriter {

	/**
	 * Directory path for CSS files.
	 */
	public static function dir(): string {
		$upload = wp_upload_dir();
		$dir    = trailingslashit( $upload['basedir'] ) . 'joc-blockora-morph';

		if ( ! is_dir( $dir ) ) {
			wp_mkdir_p( $dir );
		}

		return $dir;
	}

	/**
	 * Public URL base.
	 */
	public static function url(): string {
		$upload = wp_upload_dir();
		return trailingslashit( $upload['baseurl'] ) . 'joc-blockora-morph';
	}

	/**
	 * Write CSS for a user (editor) block.
	 *
	 * @param int                  $id            User block ID.
	 * @param string               $slug          Slug.
	 * @param mixed                $css_vars      CSS variables (flat or responsive).
	 * @param array<string, mixed> $default_attrs Optional default attrs (slides Layout CSS).
	 * @return string Relative filename.
	 */
	public static function write_user_block( int $id, string $slug, $css_vars, array $default_attrs = array(), array $field_schema = array() ): string {
		$filename = 'block-' . sanitize_file_name( $slug ) . '.css';
		$selector = '.wp-block-joc-blockora-morph-' . sanitize_title( $slug );
		self::write_file( $filename, $selector, $css_vars, 'user block #' . absint( $id ), $default_attrs, $field_schema );
		return $filename;
	}

	/**
	 * Write CSS for a JBM Library block (system template defaults).
	 *
	 * @param string               $slug          System slug.
	 * @param string               $variant       Variant key.
	 * @param mixed                $css_vars      CSS variables.
	 * @param array<string, mixed> $default_attrs Optional default attrs (slides Layout CSS).
	 * @return string Relative filename.
	 */
	public static function write_library_block( string $slug, string $variant, $css_vars, array $default_attrs = array(), array $field_schema = array() ): string {
		$filename = LibraryBlocks::css_filename( $slug, $variant );
		$selector = LibraryBlocks::css_selector( $slug, $variant );
		$comment  = 'library ' . LibraryBlocks::source_key( $slug, $variant );
		self::write_file( $filename, $selector, $css_vars, $comment, $default_attrs, $field_schema );
		return $filename;
	}

	/**
	 * Delete library block CSS.
	 *
	 * @param string $slug    System slug.
	 * @param string $variant Variant key.
	 */
	public static function delete_library_block( string $slug, string $variant ): void {
		$path = trailingslashit( self::dir() ) . LibraryBlocks::css_filename( $slug, $variant );
		if ( file_exists( $path ) ) {
			wp_delete_file( $path );
		}
	}

	/**
	 * Write CSS for a content (shortcode) block.
	 *
	 * @param string               $shortcode_key Shortcode key.
	 * @param string               $user_slug     Parent editor block slug.
	 * @param mixed                $css_vars      CSS variables.
	 * @param array<string, mixed> $content_attrs Optional content attrs (order / slides).
	 * @return string Relative filename.
	 */
	public static function write_content_block( string $shortcode_key, string $user_slug, $css_vars, array $content_attrs = array(), array $field_schema = array() ): string {
		$filename = 'content-' . sanitize_file_name( $shortcode_key ) . '.css';
		$selector = '.joc-blockora-morph-content-' . sanitize_title( $shortcode_key );
		// Also inherit editor block class for shared markup hooks.
		$selector .= ', .joc-blockora-morph-content.wp-block-joc-blockora-morph-' . sanitize_title( $user_slug ) . '.joc-blockora-morph-content-' . sanitize_title( $shortcode_key );
		self::write_file( $filename, $selector, $css_vars, 'content ' . sanitize_file_name( $shortcode_key ), $content_attrs, $field_schema );
		return $filename;
	}

	/**
	 * Delete CSS file for editor block slug.
	 *
	 * @param string $slug Slug.
	 */
	public static function delete_user_block( string $slug ): void {
		$path = trailingslashit( self::dir() ) . 'block-' . sanitize_file_name( $slug ) . '.css';
		if ( file_exists( $path ) ) {
			wp_delete_file( $path );
		}
	}

	/**
	 * Delete content CSS.
	 *
	 * @param string $shortcode_key Key.
	 */
	public static function delete_content_block( string $shortcode_key ): void {
		$path = trailingslashit( self::dir() ) . 'content-' . sanitize_file_name( $shortcode_key ) . '.css';
		if ( file_exists( $path ) ) {
			wp_delete_file( $path );
		}
	}

	/**
	 * Remove all generated Blockora Morph CSS files from uploads.
	 */
	public static function clear_all(): void {
		$dir = self::dir();
		if ( ! is_dir( $dir ) ) {
			return;
		}

		$files = glob( trailingslashit( $dir ) . '*.css' );
		if ( ! is_array( $files ) ) {
			return;
		}

		foreach ( $files as $file ) {
			if ( is_file( $file ) ) {
				wp_delete_file( $file );
			}
		}
	}

	/**
	 * Write a CSS custom-properties file (with responsive media queries).
	 *
	 * @param string               $filename      Filename.
	 * @param string               $selector      CSS selector.
	 * @param mixed                $css_vars      Vars (flat or nested).
	 * @param string               $comment       Trailing comment.
	 * @param array<string, mixed>              $default_attrs Optional attrs for per-slide Layout CSS.
	 * @param array<int, array<string, mixed>> $field_schema  Optional field schema for order defaults.
	 */
	private static function write_file( string $filename, string $selector, $css_vars, string $comment, array $default_attrs = array(), array $field_schema = array() ): void {
		$path = trailingslashit( self::dir() ) . $filename;
		$css  = CssVars::to_css( $selector, $css_vars );
		$slides = isset( $default_attrs['slides'] ) && is_array( $default_attrs['slides'] )
			? $default_attrs['slides']
			: array();
		if ( $slides ) {
			$slide_css = CssVars::to_css_for_slides( $selector, $slides, $css_vars );
			if ( '' !== trim( $slide_css ) ) {
				$css .= ( $css ? "\n\n" : '' ) . $slide_css;
			}
		}
		$order_defaults = ElementsOrder::defaults_from_schema( $field_schema, $default_attrs );
		$order_css      = ElementsOrder::to_css_for_attrs( $selector, $default_attrs, $order_defaults );
		if ( '' !== trim( $order_css ) ) {
			$css .= ( $css ? "\n\n" : '' ) . $order_css;
		}
		$vis_css = ElementsVisibility::to_css_for_attrs( $selector, $default_attrs );
		if ( '' !== trim( $vis_css ) ) {
			$css .= ( $css ? "\n\n" : '' ) . $vis_css;
		}
		if ( '' === trim( $css ) ) {
			$css = "{$selector} {\n\tfont-family: var(--bm-font-family, " . CssVars::DEFAULT_FONT_FAMILY . ");\n}";
		}
		$css .= "\n\n/* JOC Blockora Morph " . $comment . " */\n";
		self::put_contents( $path, $css );
	}

	/**
	 * Write file contents via WP_Filesystem when available.
	 *
	 * @param string $path Absolute path.
	 * @param string $contents File contents.
	 */
	private static function put_contents( string $path, string $contents ): void {
		global $wp_filesystem;

		if ( ! $wp_filesystem instanceof \WP_Filesystem_Base ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			WP_Filesystem();
		}

		if ( $wp_filesystem instanceof \WP_Filesystem_Base ) {
			$wp_filesystem->put_contents( $path, $contents, FS_CHMOD_FILE );
			return;
		}

		// Fallback when filesystem credentials are unavailable (e.g. some CLI contexts).
		file_put_contents( $path, $contents ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
	}
}
