<?php
/**
 * Document tree — Single Source of Truth.
 *
 * @package AuraBuilder
 */

declare(strict_types=1);

namespace AuraBuilder\Document;


use AuraBuilder\Support\Json;
final class Document {

	public string $id;
	public string $type;
	public string $title;
	public string $status;
	public string $version;

	/** @var array<string, mixed> */
	public array $settings;

	/** @var array<string, mixed> */
	public array $styles;

	/** @var array<string, Node> flat map id => Node */
	public array $nodes;

	/** Root node id (usually type=page/root). */
	public string $root_id;

	/** @var array<string, mixed> */
	public array $metadata;

	/**
	 * @param array<string, mixed> $data
	 */
	public function __construct( array $data = array() ) {
		$this->id       = (string) ( $data['id'] ?? 'doc_' . wp_generate_password( 8, false, false ) );
		$this->type     = sanitize_key( (string) ( $data['type'] ?? 'page' ) );
		$this->title    = sanitize_text_field( (string) ( $data['title'] ?? '' ) );
		$this->status   = sanitize_key( (string) ( $data['status'] ?? 'draft' ) );
		$this->version  = (string) ( $data['version'] ?? '1.0' );
		$this->settings = self::normalize_settings( is_array( $data['settings'] ?? null ) ? $data['settings'] : array() );
		$this->styles   = is_array( $data['styles'] ?? null ) ? $data['styles'] : array();
		$this->metadata = is_array( $data['metadata'] ?? null ) ? $data['metadata'] : array();
		$this->nodes    = array();
		$this->root_id  = '';

		if ( ! empty( $data['nodes'] ) && is_array( $data['nodes'] ) ) {
			foreach ( $data['nodes'] as $id => $node_data ) {
				if ( ! is_array( $node_data ) ) {
					continue;
				}
				$node = new Node( $node_data );
				// Prefer map key if present.
				if ( is_string( $id ) && $id !== '' && ! isset( $node_data['id'] ) ) {
					$node->id = $id;
				}
				$this->nodes[ $node->id ] = $node;
			}
			$this->root_id = (string) ( $data['rootId'] ?? $data['root_id'] ?? $this->find_root_id() );
		} elseif ( ! empty( $data['children'] ) && is_array( $data['children'] ) ) {
			// Nested tree format → flatten.
			$root = $this->ingest_tree(
				array(
					'id'       => $data['rootId'] ?? Node::generate_id(),
					'type'     => 'root',
					'children' => $data['children'],
					'settings' => array(),
				),
				null
			);
			$this->root_id = $root->id;
		} else {
			$root            = new Node(
				array(
					'id'       => Node::generate_id(),
					'type'     => 'root',
					'children' => array(),
					'settings' => array(),
				)
			);
			$this->nodes[ $root->id ] = $root;
			$this->root_id            = $root->id;
		}
	}

	public static function empty_page( string $title = '' ): self {
		$root_id = Node::generate_id();
		$sec_id  = Node::generate_id();
		$col_id  = Node::generate_id();

		$doc = new self(
			array(
				'type'     => 'page',
				'title'    => $title,
				'version'  => '1.0',
				'status'   => 'draft',
				'settings' => self::default_settings(),
				'rootId'   => $root_id,
				'nodes'    => array(
					$root_id => array(
						'id'       => $root_id,
						'type'     => 'root',
						'children' => array( $sec_id ),
						'settings' => array(),
					),
					$sec_id  => array(
						'id'       => $sec_id,
						'type'     => 'section',
						'parentId' => $root_id,
						'children' => array( $col_id ),
						'settings' => array(
							'layout' => 'boxed',
						),
					),
					$col_id  => array(
						'id'       => $col_id,
						'type'     => 'container',
						'parentId' => $sec_id,
						'children' => array(),
						'settings' => array(
							'display' => 'flex',
							'direction' => 'column',
							'gap' => '16px',
						),
					),
				),
			)
		);
		return $doc;
	}

	public function get_node( string $id ): ?Node {
		return $this->nodes[ $id ] ?? null;
	}

	public function show_header(): bool {
		$src = (string) ( $this->settings['header_source'] ?? 'global' );
		if ( $src === 'none' ) {
			return false;
		}
		if ( $src === 'theme' || $src === 'builder' ) {
			return true;
		}
		return ! empty( $this->settings['showHeader'] );
	}

	public function show_footer(): bool {
		$src = (string) ( $this->settings['footer_source'] ?? 'global' );
		if ( $src === 'none' ) {
			return false;
		}
		if ( $src === 'theme' || $src === 'builder' ) {
			return true;
		}
		return ! empty( $this->settings['showFooter'] );
	}

	private static function normalize_chrome_source( string $v ): string {
		$v = sanitize_key( $v );
		return in_array( $v, array( 'global', 'theme', 'builder', 'none' ), true ) ? $v : 'global';
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array<string, mixed>
	 */
	public static function normalize_settings( array $settings ): array {
		$show_header = true;
		$show_footer = true;
		if ( array_key_exists( 'showHeader', $settings ) ) {
			$show_header = (bool) $settings['showHeader'];
		}
		if ( array_key_exists( 'showFooter', $settings ) ) {
			$show_footer = (bool) $settings['showFooter'];
		}
		$is_home = false;
		if ( array_key_exists( 'is_home_page', $settings ) ) {
			$v = $settings['is_home_page'];
			$is_home = ( $v === true || $v === 1 || $v === '1' || $v === 'true' || $v === 'yes' );
		}
		// Papel da página no site (home / singular / arquivo / categorias).
		$page_role = sanitize_key( (string) ( $settings['page_role'] ?? ( $is_home ? 'home' : 'singular' ) ) );
		if ( ! in_array( $page_role, array( 'home', 'singular', 'archive', 'category' ), true ) ) {
			$page_role = $is_home ? 'home' : 'singular';
		}
		if ( $page_role === 'home' ) {
			$is_home = true;
		}
		// Categorias vinculadas (quando page_role = category) — IDs separados por vírgula.
		$page_cats = '';
		if ( isset( $settings['page_categories'] ) ) {
			$raw_c = is_array( $settings['page_categories'] )
				? implode( ',', $settings['page_categories'] )
				: (string) $settings['page_categories'];
			$ids   = array();
			foreach ( preg_split( '/[\s,;]+/', $raw_c ) ?: array() as $p ) {
				$n = absint( $p );
				if ( $n > 0 ) {
					$ids[] = $n;
				}
			}
			$page_cats = implode( ',', array_unique( $ids ) );
		}

		$bp_in = is_array( $settings['breakpoints'] ?? null ) ? $settings['breakpoints'] : array();
		$breakpoints = array(
			'mobile' => max( 320, min( 1200, (int) ( $bp_in['mobile'] ?? 767 ) ) ),
			'tablet' => max( 480, min( 1600, (int) ( $bp_in['tablet'] ?? 1024 ) ) ),
			'laptop' => max( 1024, min( 2560, (int) ( $bp_in['laptop'] ?? 1400 ) ) ),
		);

		$site_bg = '';
		if ( isset( $settings['site_bg'] ) && is_string( $settings['site_bg'] ) ) {
			$raw = trim( $settings['site_bg'] );
			if ( $raw !== '' && preg_match( '/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $raw ) ) {
				$site_bg = strtolower( $raw );
			} elseif ( $raw !== '' && preg_match( '/^(rgb|rgba|hsl|hsla|var)\(/i', $raw ) ) {
				$site_bg = sanitize_text_field( $raw );
			}
		}

		// Page background mode: color | image | carousel
		$bg_mode = sanitize_key( (string) ( $settings['site_bg_mode'] ?? 'color' ) );
		if ( ! in_array( $bg_mode, array( 'color', 'image', 'carousel' ), true ) ) {
			$bg_mode = 'color';
		}
		$bg_image = '';
		if ( isset( $settings['site_bg_image'] ) && is_string( $settings['site_bg_image'] ) ) {
			$bg_image = esc_url_raw( trim( $settings['site_bg_image'] ) );
		}
		// Gallery JSON for carousel (reuse string URLs / {id,url}).
		$bg_images = '[]';
		if ( isset( $settings['site_bg_images'] ) ) {
			$raw_img = is_string( $settings['site_bg_images'] ) ? $settings['site_bg_images'] : Json::encode( $settings['site_bg_images'] );
			if ( is_string( $raw_img ) && strlen( $raw_img ) < 50000 ) {
				$decoded = json_decode( $raw_img, true );
				if ( is_array( $decoded ) ) {
					$clean = array();
					foreach ( $decoded as $item ) {
						if ( is_string( $item ) ) {
							$u = esc_url_raw( $item );
							if ( $u ) {
								$clean[] = $u;
							}
						} elseif ( is_array( $item ) && ! empty( $item['url'] ) ) {
							$u  = esc_url_raw( (string) $item['url'] );
							$id = (int) ( $item['id'] ?? 0 );
							if ( $u ) {
								$clean[] = $id > 0 ? array( 'id' => $id, 'url' => $u ) : $u;
							}
						}
					}
					$bg_images = Json::encode( array_values( $clean ) ) ?: '[]';
				}
			}
		}
		// Size: cover | contain | 100% 100% | auto | custom percent e.g. 50% or 100% 80%
		$bg_size = trim( (string) ( $settings['site_bg_size'] ?? 'cover' ) );
		if ( ! in_array( $bg_size, array( 'cover', 'contain', 'auto', '100% 100%' ), true ) ) {
			// Allow percentage patterns: 50%, 100% 50%, 80% auto
			if ( ! preg_match( '/^[\d.]+%\s*([\d.]+%|auto)?$/', $bg_size ) ) {
				$bg_size = 'cover';
			}
		}
		$bg_pos = sanitize_text_field( (string) ( $settings['site_bg_position'] ?? 'center center' ) );
		if ( strlen( $bg_pos ) > 40 ) {
			$bg_pos = 'center center';
		}
		$bg_repeat = sanitize_key( (string) ( $settings['site_bg_repeat'] ?? 'no-repeat' ) );
		if ( ! in_array( $bg_repeat, array( 'no-repeat', 'repeat', 'repeat-x', 'repeat-y' ), true ) ) {
			$bg_repeat = 'no-repeat';
		}
		$bg_carousel_speed = max( 1500, min( 30000, (int) ( $settings['site_bg_carousel_speed'] ?? 5000 ) ) );
		$bg_carousel_fx    = sanitize_key( (string) ( $settings['site_bg_carousel_fx'] ?? 'fade' ) );
		if ( ! in_array( $bg_carousel_fx, array( 'fade', 'slide', 'zoom' ), true ) ) {
			$bg_carousel_fx = 'fade';
		}

		return array_merge(
			$settings,
			array(
				'showHeader'              => $show_header,
				'showFooter'              => $show_footer,
				'is_home_page'            => $is_home,
				'page_role'               => $page_role,
				'page_categories'         => $page_cats,
				'header_source'           => self::normalize_chrome_source( (string) ( $settings['header_source'] ?? 'global' ) ),
				'footer_source'           => self::normalize_chrome_source( (string) ( $settings['footer_source'] ?? 'global' ) ),
				'breakpoints'             => $breakpoints,
				'site_bg'                 => $site_bg,
				'site_bg_mode'            => $bg_mode,
				'site_bg_image'           => $bg_image,
				'site_bg_images'          => $bg_images,
				'site_bg_size'            => $bg_size,
				'site_bg_position'        => $bg_pos,
				'site_bg_repeat'          => $bg_repeat,
				'site_bg_carousel_speed'  => $bg_carousel_speed,
				'site_bg_carousel_fx'     => $bg_carousel_fx,
			)
		);
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function default_settings(): array {
		return array(
			'showHeader'             => true,
			'showFooter'             => true,
			'is_home_page'           => false,
			'page_role'              => 'singular',
			'page_categories'        => '',
			// theme | builder | none | global (usa Theme Builder)
			'header_source'          => 'global',
			'footer_source'          => 'global',
			'breakpoints'            => array(
				'mobile' => 767,
				'tablet' => 1024,
				'laptop' => 1400,
			),
			'site_bg'                => '',
			'site_bg_mode'           => 'color',
			'site_bg_image'          => '',
			'site_bg_images'         => '[]',
			'site_bg_size'           => 'cover',
			'site_bg_position'       => 'center center',
			'site_bg_repeat'         => 'no-repeat',
			'site_bg_carousel_speed' => 5000,
			'site_bg_carousel_fx'    => 'fade',
		);
	}

	/**
	 * @return array<string, mixed>
	 */
	public function to_array(): array {
		$nodes = array();
		foreach ( $this->nodes as $id => $node ) {
			$nodes[ $id ] = $node->to_array();
		}
		return array(
			'id'       => $this->id,
			'type'     => $this->type,
			'title'    => $this->title,
			'status'   => $this->status,
			'version'  => $this->version,
			'settings' => $this->settings,
			'styles'   => $this->styles,
			'rootId'   => $this->root_id,
			'nodes'    => $nodes,
			'metadata' => $this->metadata,
		);
	}

	/**
	 * @param array<string, mixed> $tree
	 */
	private function ingest_tree( array $tree, ?string $parent_id ): Node {
		$node              = new Node( $tree );
		$node->parent_id   = $parent_id;
		$child_trees       = isset( $tree['children'] ) && is_array( $tree['children'] ) ? $tree['children'] : array();
		$node->children    = array();
		$this->nodes[ $node->id ] = $node;

		foreach ( $child_trees as $child ) {
			if ( ! is_array( $child ) ) {
				continue;
			}
			// Nested object children.
			if ( isset( $child['type'] ) ) {
				$c = $this->ingest_tree( $child, $node->id );
				$node->children[] = $c->id;
			} elseif ( is_string( $child ) ) {
				$node->children[] = $child;
			}
		}
		return $node;
	}

	private function find_root_id(): string {
		foreach ( $this->nodes as $node ) {
			if ( $node->parent_id === null || $node->type === 'root' ) {
				return $node->id;
			}
		}
		$first = array_key_first( $this->nodes );
		return is_string( $first ) ? $first : '';
	}
}
