<?php

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

require_once AIEO_DIR . 'includes/seo/class-aieo-seo-store.php';

class AIEO_SEO_Importer {

	
	private static $direct_map = array(
		'title'                    => 'title',
		'description'              => 'description',
		'keyphrases'               => 'keyphrases',
		'og_title'                 => 'og_title',
		'og_description'           => 'og_description',
		'og_object_type'           => 'og_object_type',
		'og_image_type'            => 'og_image_type',
		'og_image_custom_url'      => 'og_image_custom_url',
		'og_article_section'       => 'og_article_section',
		'og_article_tags'          => 'og_article_tags',
		'twitter_use_og'           => 'twitter_use_og',
		'twitter_card'             => 'twitter_card',
		'twitter_image_type'       => 'twitter_image_type',
		'twitter_image_custom_url' => 'twitter_image_custom_url',
		'twitter_title'            => 'twitter_title',
		'twitter_description'      => 'twitter_description',
		'schema_type_options'      => 'schema_type_options',
		'pillar_content'           => 'pillar_content',
		'robots_default'           => 'robots_default',
		'robots_noindex'           => 'robots_noindex',
		'robots_nofollow'          => 'robots_nofollow',
		'robots_noarchive'         => 'robots_noarchive',
		'robots_nosnippet'         => 'robots_nosnippet',
		'robots_noimageindex'      => 'robots_noimageindex',
		'robots_noodp'             => 'robots_noodp',
		'robots_notranslate'       => 'robots_notranslate',
		'robots_max_snippet'       => 'robots_max_snippet',
		'robots_max_videopreview'  => 'robots_max_videopreview',
		'robots_max_imagepreview'  => 'robots_max_imagepreview',
	);

	
	private static $our_schema_types = array( 'Article', 'Product', 'WebPage', 'default' );

	public static function aioseo_posts_table() {
		global $wpdb;
		return $wpdb->prefix . 'aioseo_posts';
	}

	public static function aioseo_terms_table() {
		global $wpdb;
		return $wpdb->prefix . 'aioseo_terms';
	}

	public static function table_exists( $t ) {
		global $wpdb;
		return (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) === $t;
	}

	
	public static function available() {
		return self::table_exists( self::aioseo_posts_table() );
	}

	
	public static function counts() {
		global $wpdb;
		$out = array( 'posts' => 0, 'terms' => 0 );
		$pt  = self::aioseo_posts_table();
		$tt  = self::aioseo_terms_table();
		if ( self::table_exists( $pt ) ) {
			
			
			
			
			
			
			$out['posts'] = (int) $wpdb->get_var( "SELECT COUNT(DISTINCT a.post_id) FROM `{$pt}` a INNER JOIN {$wpdb->posts} p ON p.ID = a.post_id" );
		}
		if ( self::table_exists( $tt ) ) {
			
			
			
			
			$out['terms'] = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$tt}` at INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = at.term_id" );
		}
		return $out;
	}

	
	
	

	
	public static function import_posts_chunk( $after_id, $limit ) {
		$out = array( 'imported' => 0, 'stripped' => 0, 'skipped' => 0, 'last_id' => (int) $after_id, 'done' => true );
		$t   = self::aioseo_posts_table();
		if ( ! self::table_exists( $t ) || ! class_exists( 'AIEO_SEO_Store' ) ) {
			return $out;
		}
		global $wpdb;
		
		
		
		
		
		
		$rows  = $wpdb->get_results( $wpdb->prepare(
			"SELECT a.*, p.post_type AS aieo_post_type FROM `{$t}` a
			 INNER JOIN {$wpdb->posts} p ON p.ID = a.post_id
			 WHERE a.id > %d ORDER BY a.id ASC LIMIT %d",
			(int) $after_id,
			(int) $limit
		), ARRAY_A );
		$count = is_array( $rows ) ? count( $rows ) : 0;
		foreach ( (array) $rows as $r ) {
			$out['last_id'] = (int) $r['id'];
			$post_id        = (int) $r['post_id'];
			$type           = (string) $r['aieo_post_type'];
			if ( $post_id <= 0 || '' === $type ) {
				$out['skipped']++;
				continue;
			}
			list( $fields, $stripped ) = self::map_row( $r, true );
			AIEO_SEO_Store::save( $type, $post_id, $fields );
			if ( $stripped ) {
				$out['stripped']++;
			}
			$out['imported']++;
		}
		
		
		
		$out['done'] = ( $count < (int) $limit );
		return $out;
	}

	
	public static function import_terms_chunk( $after_id, $limit ) {
		$out = array( 'imported' => 0, 'stripped' => 0, 'skipped' => 0, 'last_id' => (int) $after_id, 'done' => true );
		$t   = self::aioseo_terms_table();
		if ( ! self::table_exists( $t ) || ! class_exists( 'AIEO_SEO_Store' ) ) {
			return $out;
		}
		global $wpdb;
		
		
		$rows  = $wpdb->get_results( $wpdb->prepare(
			"SELECT at.*, tt.taxonomy FROM `{$t}` at
			 INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = at.term_id
			 WHERE at.id > %d ORDER BY at.id ASC LIMIT %d",
			(int) $after_id,
			(int) $limit
		), ARRAY_A );
		$count = is_array( $rows ) ? count( $rows ) : 0;
		foreach ( (array) $rows as $r ) {
			$out['last_id'] = (int) $r['id'];
			$term_id        = (int) $r['term_id'];
			$tax            = (string) $r['taxonomy'];
			if ( $term_id <= 0 || '' === $tax ) {
				$out['skipped']++;
				continue;
			}
			list( $fields, $stripped ) = self::map_row( $r, false );
			AIEO_SEO_Store::save( $tax, $term_id, $fields );
			if ( $stripped ) {
				$out['stripped']++;
			}
			$out['imported']++;
		}
		$out['done'] = ( $count < (int) $limit );
		return $out;
	}

	
	private static function map_row( $r, $is_post ) {
		$fields   = array();
		$stripped = false;

		foreach ( self::$direct_map as $src => $dst ) {
			if ( array_key_exists( $src, $r ) && null !== $r[ $src ] ) {
				$fields[ $dst ] = $r[ $src ];
			}
		}

		
		if ( ! empty( $r['canonical_url'] ) ) {
			$fields['canonical_override'] = (string) $r['canonical_url'];
		}

		if ( $is_post ) {
			
			$focus = self::focus_from_keyphrases( isset( $r['keyphrases'] ) ? (string) $r['keyphrases'] : '' );
			if ( '' !== $focus ) {
				$fields['focus_keyphrase']        = $focus;
				$fields['focus_keyphrase_source'] = 'imported';
			}

			
			$st = isset( $r['schema_type'] ) ? (string) $r['schema_type'] : '';
			if ( 'FAQPage' === $st || 'HowTo' === $st ) {
				$st       = 'WebPage';
				$stripped = true;
			}
			if ( '' !== $st && ! in_array( $st, self::$our_schema_types, true ) ) {
				$st = 'default';
			}
			if ( '' !== $st ) {
				$fields['schema_type'] = $st;
			}

			
			$pt = isset( $r['primary_term'] ) ? (string) $r['primary_term'] : '';
			if ( '' !== $pt ) {
				$map = json_decode( $pt, true );
				if ( is_array( $map ) && ! empty( $map ) ) {
					$fields['primary_term'] = wp_json_encode( $map );
					if ( ! empty( $map['product_cat'] ) ) {
						$fields['primary_cat_id'] = (int) $map['product_cat'];
					} elseif ( ! empty( $map['category'] ) ) {
						$fields['primary_cat_id'] = (int) $map['category'];
					}
				}
			}
		}

		return array( $fields, $stripped );
	}

	private static function focus_from_keyphrases( $json ) {
		if ( '' === (string) $json ) {
			return '';
		}
		$d = json_decode( (string) $json, true );
		if ( is_array( $d ) && isset( $d['focus']['keyphrase'] ) ) {
			return (string) $d['focus']['keyphrase'];
		}
		return '';
	}

	
	
	

	
	public static function import_options() {
		if ( ! class_exists( 'AIEO_DMM_Settings' ) ) {
			return array( 'imported' => false, 'post_types' => 0, 'taxonomies' => 0 );
		}
		$opts = self::read_aioseo_option( 'aioseo_options' );
		$dyn  = self::read_aioseo_option( 'aioseo_options_dynamic' );
		if ( empty( $opts ) && empty( $dyn ) ) {
			return array( 'imported' => false, 'post_types' => 0, 'taxonomies' => 0 );
		}

		$seo = (array) AIEO_DMM_Settings::get( 'seo', array() );

		
		$sep = self::dig( $opts, array( 'searchAppearance', 'global', 'separator' ) );
		if ( null !== $sep && '' !== (string) $sep ) {
			$d = html_entity_decode( (string) $sep, ENT_QUOTES, 'UTF-8' );
			$seo['separator'] = '' !== trim( $d ) ? $d : '-';
		}

		
		$schema      = (array) ( self::dig( $opts, array( 'searchAppearance', 'global', 'schema' ) ) ?: array() );
		$represents  = (string) self::dig_v( $schema, 'siteRepresents', 'organization' );
		$is_person   = ( 'person' === $represents );
		$seo['org_represents'] = $is_person ? 'person' : 'organization';
		$org_name    = $is_person ? self::dig_v( $schema, 'personName', '' ) : self::dig_v( $schema, 'organizationName', '' );
		if ( '' !== (string) $org_name ) {
			$seo['org_name'] = self::expand_setting_tokens( (string) $org_name, $seo['separator'] ?? '-' );
		}
		$org_logo = $is_person ? self::dig_v( $schema, 'personLogo', '' ) : self::dig_v( $schema, 'organizationLogo', '' );
		if ( '' !== (string) $org_logo ) {
			$seo['org_logo'] = (string) $org_logo;
		}
		$phone = self::dig_v( $schema, 'phone', '' );
		if ( '' !== (string) $phone ) {
			$seo['org_phone'] = (string) $phone;
		}

		
		$urls = (array) ( self::dig( $opts, array( 'social', 'profiles', 'urls' ) ) ?: array() );
		$same = array();
		foreach ( $urls as $u ) {
			$u = trim( (string) $u );
			if ( '' !== $u ) {
				$same[] = $u;
			}
		}
		
		$extra = self::dig( $opts, array( 'social', 'profiles', 'additionalUrls' ) );
		if ( is_string( $extra ) && '' !== trim( $extra ) ) {
			foreach ( preg_split( '/\s+/', $extra ) as $u ) {
				$u = trim( (string) $u );
				if ( '' !== $u ) {
					$same[] = $u;
				}
			}
		}
		if ( ! empty( $same ) ) {
			$seo['social_urls'] = array_values( array_unique( $same ) );
		}

		
		$og_img = self::dig( $opts, array( 'social', 'facebook', 'general', 'defaultImagePosts' ) );
		if ( $og_img ) {
			$seo['og_default_image'] = (string) $og_img;
		}

		
		
		
		
		$img_alt = self::dig( $opts, array( 'image', 'format', 'altTag' ) );
		if ( null === $img_alt ) { $img_alt = self::dig( $opts, array( 'searchAppearance', 'image', 'altTag' ) ); }
		if ( is_string( $img_alt ) && '' !== trim( $img_alt ) ) {
			$fmt = trim( strtr( (string) $img_alt, array(
				'#image_title'      => '#title',
				'#attachment_title' => '#title',
				'#alt_tag'          => '#title',
				'#filename'         => '#title',
				'#caption'          => '',
				'#description'      => '',
			) ) );
			if ( '' !== $fmt ) {
				$seo['image_alt_format']  = $fmt;
				$seo['image_alt_enabled'] = true;
			}
		}

		
		
		
		
		$gr       = (array) ( self::dig( $opts, array( 'searchAppearance', 'advanced', 'globalRobotsMeta' ) ) ?: array() );
		$existing = class_exists( 'AIEO_DMM_Settings' ) ? AIEO_DMM_Settings::get( 'seo.robots_global', array() ) : array();
		$seo['robots_global'] = array_merge(
			is_array( $existing ) ? $existing : array(),
			array(
				'noindex'            => ! empty( $gr['noindex'] ),
				'noindex_paginated'  => ! empty( $gr['noindexPaginated'] ),
				'nofollow_paginated' => ! empty( $gr['nofollowPaginated'] ),
			)
		);

		
		
		$niec = self::dig( $opts, array( 'searchAppearance', 'advanced', 'noIndexEmptyCat' ) );
		$seo['noindex_empty_term'] = ( null === $niec ) ? true : ! empty( $niec );

		
		
		
		$seo['post_types'] = array();
		foreach ( (array) ( self::dig( $dyn, array( 'searchAppearance', 'postTypes' ) ) ?: array() ) as $type => $cfg ) {
			
			$name = ( '_aioseo_type' === (string) $type ) ? 'type' : (string) $type;
			$seo['post_types'][ $name ] = self::map_type_cfg( (array) $cfg, $gr );
		}
		$seo['taxonomies'] = array();
		foreach ( (array) ( self::dig( $dyn, array( 'searchAppearance', 'taxonomies' ) ) ?: array() ) as $tax => $cfg ) {
			$name = ( '_aioseo_type' === (string) $tax ) ? 'type' : (string) $tax;
			$seo['taxonomies'][ $name ] = self::map_type_cfg( (array) $cfg, $gr );
		}

		
		
		
		
		$all        = AIEO_DMM_Settings::get_all();
		$all['seo'] = $seo;
		AIEO_DMM_Settings::replace( $all );

		return array(
			'imported'   => true,
			'post_types' => count( $seo['post_types'] ),
			'taxonomies' => count( $seo['taxonomies'] ),
		);
	}

	
	private static function map_type_cfg( $cfg, $global_robots ) {
		$rm      = isset( $cfg['advanced']['robotsMeta'] ) ? (array) $cfg['advanced']['robotsMeta'] : array();
		$show    = isset( $cfg['show'] ) ? ! empty( $cfg['show'] ) : true;
		$inherit = isset( $rm['default'] ) ? ! empty( $rm['default'] ) : true;
		
		
		
		
		
		
		
		$eff = $inherit ? (array) $global_robots : $rm;
		
		
		
		$eff_default = array_key_exists( 'default', $eff ) ? ! empty( $eff['default'] ) : true;
		$noindex     = ( ! $show ) || ( ! $eff_default && ! empty( $eff['noindex'] ) );

		return array(
			'title'       => isset( $cfg['title'] ) ? (string) $cfg['title'] : '',
			'description' => isset( $cfg['metaDescription'] ) ? (string) $cfg['metaDescription'] : '',
			'schema_type' => isset( $cfg['schemaType'] ) ? (string) $cfg['schemaType'] : '',
			'noindex'     => (bool) $noindex,
		);
	}

	
	private static function read_aioseo_option( $name ) {
		$raw = get_option( $name );
		if ( is_array( $raw ) ) {
			return $raw;
		}
		if ( is_string( $raw ) && '' !== $raw ) {
			$d = json_decode( $raw, true );
			return is_array( $d ) ? $d : array();
		}
		return array();
	}

	
	private static function dig( $arr, $path ) {
		$node = $arr;
		foreach ( (array) $path as $k ) {
			if ( ! is_array( $node ) || ! array_key_exists( $k, $node ) ) {
				return null;
			}
			$node = $node[ $k ];
		}
		return $node;
	}

	private static function dig_v( $arr, $key, $default ) {
		return ( is_array( $arr ) && isset( $arr[ $key ] ) ) ? $arr[ $key ] : $default;
	}

	
	private static function expand_setting_tokens( $str, $sep ) {
		return trim( strtr( $str, array(
			'#site_title'   => get_bloginfo( 'name' ),
			'#tagline'      => get_bloginfo( 'description' ),
			'#separator_sa' => (string) $sep,
		) ) );
	}

	
	
	

	
	public static function mapping_report() {
		return array(
			'title / description'        => array( 'status' => 'mapped',      'detail' => 'copied verbatim incl. #smart-tags (vocabulary adopted as-is)' ),
			'canonical_url'              => array( 'status' => 'transformed', 'detail' => '→ canonical_override (our canonical_url is the materialised path)' ),
			'primary_term'               => array( 'status' => 'transformed', 'detail' => '→ primary_term JSON + flat primary_cat_id' ),
			'keyphrases'                 => array( 'status' => 'mapped',      'detail' => 'copied; focus keyphrase denormalised to focus_keyphrase' ),
			'robots_* (per-object flags)' => array( 'status' => 'transformed', 'detail' => 'EXPLICIT per-object noindex/nofollow (robots_default=0) preserved exactly; inherited type-defaults are handled separately (next row)' ),
			'robots inherited type defaults' => array( 'status' => 'mapped', 'detail' => 'Imported (Track 2): effective per-type/taxonomy noindex resolved from show + robotsMeta inheritance; the builder applies it when robots_default inherits. Risk #6 closed.' ),
			'orphaned term-meta'         => array( 'status' => 'dropped',     'detail' => 'aioseo_terms rows whose term no longer exists — skipped (nothing to render)' ),
			'og_* / twitter_*'           => array( 'status' => 'transformed', 'detail' => 'custom_url + type copied; image URLs RE-DERIVED (AIOSEO derives them at save)' ),
			'schema_type'                => array( 'status' => 'transformed', 'detail' => 'mapped to Product/Article/WebPage; out-of-set → WebPage' ),
			'FAQPage / HowTo schema'     => array( 'status' => 'dropped',     'detail' => 'STRIPPED — no longer rich-result-eligible (FAQ ended 2026-05-07)' ),
			'Image SEO alt format'       => array( 'status' => 'transformed', 'detail' => 'AIOSEO Image-SEO alt-tag format → seo.image_alt_format (image-only tags remapped to #title); existing attachment alts are preserved (we fill only empties)' ),
			'pillar_content'             => array( 'status' => 'mapped',      'detail' => 'copied (AIEO also auto-derives pillars from performance)' ),
			'og_image_url / *_width/height / twitter_image_url' => array( 'status' => 'dropped', 'detail' => 'derived values — recomputed by our builder, not copied' ),
			'page_analysis / seo_score'  => array( 'status' => 'dropped',     'detail' => 'recomputed by our own §4a demand-aware score' ),
			'SEO Revisions / Search Statistics / Writing Assistant' => array( 'status' => 'dropped', 'detail' => 'not migrated (out of scope)' ),
			'redirects (aioseo_redirects)' => array( 'status' => 'deferred',  'detail' => 'imported by the Redirects tool (Phase A2) before cut-over' ),
			'global settings (aioseo_options)' => array( 'status' => 'mapped', 'detail' => 'Imported: separator, Organization/Person schema, social sameAs, default OG image, global robots, per-type+taxonomy title/desc formats + schemaType.' ),
		);
	}
}
