<?php
/**
 * #2 On-page Optimization — checklist tự động chấm, lưu snapshot trước/sau.
 * Require từ seo-console.php.
 */
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

define( 'TTSEO_OP_DBVER', '1' );

function ttseo_op_table() {
	global $wpdb;
	return $wpdb->prefix . 'ttseo_onpage';
}
function ttseo_op_install() {
	if ( get_option( 'ttseo_op_dbver' ) === TTSEO_OP_DBVER ) {
		return;
	}
	global $wpdb;
	$table   = ttseo_op_table();
	$charset = $wpdb->get_charset_collate();
	require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	dbDelta( "CREATE TABLE $table (
		id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
		url VARCHAR(255) NOT NULL DEFAULT '',
		score TINYINT NOT NULL DEFAULT 0,
		passed TINYINT NOT NULL DEFAULT 0,
		total TINYINT NOT NULL DEFAULT 0,
		results LONGTEXT NULL,
		created_at DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00',
		PRIMARY KEY (id),
		KEY url (url)
	) $charset;" );
	update_option( 'ttseo_op_dbver', TTSEO_OP_DBVER );
}
add_action( 'admin_init', 'ttseo_op_install' );

/* =========================================================================
 * BỘ CHẤM ON-PAGE — tải URL, phân tích HTML
 * ========================================================================= */
function ttseo_op_audit( $url ) {
	$resp = wp_remote_get( $url, array( 'timeout' => 25, 'user-agent' => 'TTSEO-Dashboard/1.0' ) );
	if ( is_wp_error( $resp ) ) {
		return $resp;
	}
	$code = wp_remote_retrieve_response_code( $resp );
	if ( 200 !== $code ) {
		return new WP_Error( 'http', ttseo_t( 'URL trả về HTTP ', 'URL returned HTTP ' ) . $code );
	}
	$html = wp_remote_retrieve_body( $resp );
	if ( ! $html ) {
		return new WP_Error( 'empty', ttseo_t( 'Không tải được nội dung.', 'Could not fetch content.' ) );
	}

	// Từ khóa đích đã gán (nếu có) để kiểm tra tiêu đề
	global $wpdb;
	$kws = array();
	$kt  = $wpdb->prefix . 'ttseo_keywords';
	if ( $wpdb->get_var( "SHOW TABLES LIKE '$kt'" ) === $kt ) {
		$kws = $wpdb->get_col( $wpdb->prepare( "SELECT keyword FROM $kt WHERE target_url=%s ORDER BY priority ASC", $url ) );
	}
	$kw = $kws[0] ?? '';

	$prev = libxml_use_internal_errors( true );
	$dom  = new DOMDocument();
	$dom->loadHTML( '<?xml encoding="utf-8" ?>' . $html );
	libxml_clear_errors();
	libxml_use_internal_errors( $prev );
	$xp = new DOMXPath( $dom );

	$host = wp_parse_url( home_url(), PHP_URL_HOST );
	$R    = array();
	$missing = ttseo_t( 'THIẾU', 'MISSING' );
	$yes     = ttseo_t( 'Có', 'Yes' );

	// 1. Title
	$title = '';
	$t     = $xp->query( '//title' );
	if ( $t->length ) { $title = trim( $t->item( 0 )->textContent ); }
	$tl = mb_strlen( $title );
	$R[] = array(
		'id'   => 'title',
		'k'    => ttseo_t( 'Thẻ tiêu đề (title)', 'Title tag' ),
		'ok'   => ( $tl >= 30 && $tl <= 65 ),
		'v'    => $tl ? $tl . ttseo_t( ' ký tự: "', ' characters: "' ) . mb_substr( $title, 0, 60 ) . '"' : $missing,
		'hint' => ttseo_t( 'Nên 30–65 ký tự.', 'Should be 30–65 characters.' ),
	);

	// 2. Meta description
	$md = '';
	$m  = $xp->query( '//meta[@name="description"]/@content' );
	if ( $m->length ) { $md = trim( $m->item( 0 )->nodeValue ); }
	$ml = mb_strlen( $md );
	$R[] = array(
		'id'   => 'meta_desc',
		'k'    => ttseo_t( 'Mô tả tìm kiếm (meta description)', 'Meta description' ),
		'ok'   => ( $ml >= 70 && $ml <= 165 ),
		'v'    => $ml ? $ml . ttseo_t( ' ký tự', ' characters' ) : $missing,
		'hint' => ttseo_t( 'Nên 70–165 ký tự.', 'Should be 70–165 characters.' ),
	);

	// 3. H1 duy nhất
	$h1 = $xp->query( '//h1' );
	$R[] = array(
		'id'   => 'h1',
		'k'    => ttseo_t( 'Thẻ H1 duy nhất', 'Single H1 tag' ),
		'ok'   => ( 1 === $h1->length ),
		'v'    => $h1->length . ttseo_t( ' thẻ H1', ' H1 tag(s)' ) . ( $h1->length ? ': "' . mb_substr( trim( $h1->item( 0 )->textContent ), 0, 50 ) . '"' : '' ),
		'hint' => ttseo_t( 'Mỗi trang đúng 1 H1.', 'Exactly one H1 per page.' ),
	);

	// 4. Từ khóa đích trong H1/title — đạt nếu MỌI từ của 1 keyword có mặt (chuẩn hóa gạch nối + số nhiều)
	if ( $kws ) {
		$h1txt = $h1->length ? $h1->item( 0 )->textContent : '';
		$norm  = function ( $s ) {
			$s = strtolower( $s );
			$s = str_replace( array( '-', '/', '&' ), ' ', $s );
			$s = preg_replace( '/[^a-z0-9\s]/', ' ', $s );
			$s = preg_replace( '/\s+/', ' ', $s );
			$words = array_filter( explode( ' ', trim( $s ) ) );
			return array_map( function ( $w ) { return rtrim( $w, 's' ); }, $words ); // bỏ 's' số nhiều
		};
		$hay   = array_merge( $norm( $title ), $norm( $h1txt ) );
		$stop  = array( 'in', 'the', 'and', 'for', 'of', 'a', 'to', 'per', 'corp', 'truong', 'thinh' );
		$found = '';
		foreach ( $kws as $k ) {
			$words = array_diff( $norm( $k ), $stop );
			if ( ! $words ) { continue; }
			$miss = array_diff( $words, $hay );
			if ( empty( $miss ) ) { $found = $k; break; }
		}
		$R[] = array(
			'id'   => 'target_kw',
			'k'    => ttseo_t( 'Từ khóa đích trong Title/H1', 'Target keyword in Title/H1' ),
			'ok'   => (bool) $found,
			'v'    => $found ? $yes . ' "' . $found . '"' : ttseo_t( 'Thiếu (', 'Missing (' ) . count( $kws ) . ttseo_t( ' từ khóa đã gán)', ' keyword(s) assigned)' ),
			'hint' => ttseo_t( 'Theo bảng Keyword Mapping.', 'Based on the Keyword Mapping table.' ),
		);
	}

	// 5. Phân cấp heading không nhảy bậc
	$hs = $xp->query( '//h1|//h2|//h3|//h4|//h5|//h6' );
	$skip = false; $last = 0;
	foreach ( $hs as $h ) {
		$lv = (int) substr( $h->nodeName, 1 );
		if ( $last && $lv > $last + 1 ) { $skip = true; break; }
		$last = $lv;
	}
	$R[] = array(
		'id'   => 'heading_hier',
		'k'    => ttseo_t( 'Phân cấp heading', 'Heading hierarchy' ),
		'ok'   => ! $skip,
		'v'    => $hs->length . ttseo_t( ' heading', ' heading(s)' ) . ( $skip ? ttseo_t( ' — CÓ nhảy bậc', ' — has skipped level(s)' ) : ttseo_t( ' — đúng thứ tự', ' — correct order' ) ),
		'hint' => ttseo_t( 'Không nhảy H2→H4.', 'Do not skip e.g. H2→H4.' ),
	);

	// 6. Liên kết nội bộ
	$int = 0;
	foreach ( $xp->query( '//a/@href' ) as $a ) {
		$h = $a->nodeValue;
		if ( 0 === strpos( $h, '/' ) || false !== strpos( $h, $host ) ) { $int++; }
	}
	$R[] = array(
		'id'   => 'internal_links',
		'k'    => ttseo_t( 'Liên kết nội bộ', 'Internal links' ),
		'ok'   => ( $int >= 3 ),
		'v'    => $int . ttseo_t( ' liên kết', ' link(s)' ),
		'hint' => ttseo_t( 'Tối thiểu 3 liên kết nội bộ.', 'At least 3 internal links.' ),
	);

	// 7. Ảnh có alt
	$imgs = $xp->query( '//img' ); $noalt = 0;
	foreach ( $imgs as $img ) {
		$alt = $img->getAttribute( 'alt' );
		if ( '' === trim( $alt ) && ! $img->hasAttribute( 'role' ) ) { $noalt++; }
	}
	$R[] = array(
		'id'   => 'img_alt',
		'k'    => ttseo_t( 'Ảnh có thẻ alt', 'Images have alt text' ),
		'ok'   => ( 0 === $noalt ),
		'v'    => $imgs->length . ttseo_t( ' ảnh, ', ' image(s), ' ) . $noalt . ttseo_t( ' thiếu alt', ' missing alt' ),
		'hint' => ttseo_t( 'Mọi ảnh nội dung cần alt.', 'Every content image needs alt text.' ),
	);

	// 8. Dữ liệu có cấu trúc
	$ld = $xp->query( '//script[@type="application/ld+json"]' );
	$R[] = array(
		'id'   => 'schema',
		'k'    => ttseo_t( 'Dữ liệu có cấu trúc (Schema)', 'Structured data (Schema)' ),
		'ok'   => ( $ld->length > 0 ),
		'v'    => $ld->length . ttseo_t( ' khối JSON-LD', ' JSON-LD block(s)' ),
		'hint' => ttseo_t( 'Cần schema phù hợp loại trang.', 'Add schema matching the page type.' ),
	);

	// 9. Canonical
	$cn = $xp->query( '//link[@rel="canonical"]/@href' );
	$R[] = array(
		'id'   => 'canonical',
		'k'    => ttseo_t( 'Thẻ canonical', 'Canonical tag' ),
		'ok'   => ( $cn->length > 0 ),
		'v'    => $cn->length ? $yes : $missing,
		'hint' => ttseo_t( 'Tránh trùng lặp nội dung.', 'Avoid duplicate content.' ),
	);

	// 10. CTA (lời kêu gọi hành động)
	$cta = preg_match( '/(get a quote|request a quote|quotation|contact us|get in touch|free quote|báo giá|liên hệ|request quote|send request|submit request)/i', wp_strip_all_tags( $html ) );
	$R[] = array(
		'id'   => 'cta',
		'k'    => ttseo_t( 'Lời kêu gọi hành động (CTA)', 'Call-to-action (CTA)' ),
		'ok'   => (bool) $cta,
		'v'    => $cta ? ttseo_t( 'Có CTA', 'Has a CTA' ) : ttseo_t( 'Không tìm thấy', 'Not found' ),
		'hint' => ttseo_t( 'Cần nút/liên kết báo giá – liên hệ.', 'Add a quote/contact button or link.' ),
	);

	// 11. Độ dài nội dung
	$body = $xp->query( '//body' );
	$wc   = 0;
	if ( $body->length ) {
		$txt = preg_replace( '/\s+/', ' ', wp_strip_all_tags( $dom->saveHTML( $body->item( 0 ) ) ) );
		$wc  = str_word_count( $txt );
	}
	$R[] = array(
		'id'   => 'wordcount',
		'k'    => ttseo_t( 'Độ dài nội dung', 'Content length' ),
		'ok'   => ( $wc >= 300 ),
		'v'    => number_format_i18n( $wc ) . ttseo_t( ' từ', ' words' ),
		'hint' => ttseo_t( 'Tối thiểu ~300 từ.', 'At least ~300 words.' ),
	);

	$passed = 0;
	foreach ( $R as $r ) { if ( $r['ok'] ) { $passed++; } }
	$total = count( $R );

	return array(
		'url'    => $url,
		'passed' => $passed,
		'total'  => $total,
		'score'  => $total ? (int) round( $passed / $total * 100 ) : 0,
		'items'  => $R,
	);
}

/** Chạy audit + lưu snapshot */
function ttseo_op_run_save( $url ) {
	$res = ttseo_op_audit( $url );
	if ( is_wp_error( $res ) ) {
		return $res;
	}
	global $wpdb;
	$wpdb->insert( ttseo_op_table(), array(
		'url' => $url, 'score' => $res['score'], 'passed' => $res['passed'], 'total' => $res['total'],
		'results' => wp_json_encode( $res['items'] ), 'created_at' => current_time( 'mysql' ),
	) );
	return $res;
}

/* =========================================================================
 * MENU + HANDLERS
 * On-page Checklist đã GỘP vào "Công việc SEO" (tab On-page Optimization) —
 * không còn là trang riêng, tránh trùng với tab quota cũ chỉ ghi log thủ công.
 * Giữ redirect cho bookmark/link cũ trỏ tới ?page=ttseo-onpage.
 * ========================================================================= */
add_action( 'admin_init', function () {
	if ( isset( $_GET['page'] ) && 'ttseo-onpage' === $_GET['page'] && ttseo_access() ) {
		$args = $_GET;
		unset( $args['page'] );
		$args['page'] = 'ttseo-work';
		$args['tab']  = 'onpage';
		wp_safe_redirect( admin_url( 'admin.php?' . http_build_query( $args ) ) );
		exit;
	}
} );

add_action( 'admin_init', function () {
	if ( ! ttseo_access() || ! function_exists( 'ttseo_is_unlocked' ) || ! ttseo_is_unlocked() ) {
		return;
	}
	if ( isset( $_POST['ttseo_op_run'] ) ) {
		check_admin_referer( 'ttseo_op_run' );
		$url = esc_url_raw( wp_unslash( $_POST['op_url'] ?? '' ) );
		if ( $url ) {
			$res = ttseo_op_run_save( $url );
			if ( is_wp_error( $res ) ) {
				set_transient( 'ttseo_op_err', $res->get_error_message(), 60 );
			}
			// ghi vào nhật ký On-page (nghiệm thu 15 URL/tháng)
			if ( ! is_wp_error( $res ) ) {
				global $wpdb;
				$t   = ttseo_wf_table();
				$now = current_time( 'mysql' );
				$ex  = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $t WHERE type='onpage' AND url=%s", $url ) );
				$note = ttseo_t( 'Điểm on-page ', 'On-page score ' ) . $res['score'] . '/100 (' . $res['passed'] . '/' . $res['total'] . ttseo_t( ' tiêu chí đạt)', ' criteria passed)' );
				if ( $ex ) {
					$wpdb->update( $t, array( 'status' => ( 100 === $res['score'] ? 'done' : 'checking' ), 'notes' => $note, 'updated_at' => $now ), array( 'id' => $ex ) );
				} else {
					$wpdb->insert( $t, array( 'type' => 'onpage', 'title' => $url, 'url' => $url, 'status' => ( 100 === $res['score'] ? 'done' : 'checking' ), 'period' => gmdate( 'Y-m' ), 'notes' => $note, 'created_at' => $now, 'updated_at' => $now ) );
				}
			}
		}
		wp_safe_redirect( admin_url( 'admin.php?page=ttseo-work&tab=onpage&u=' . rawurlencode( $url ) ) );
		exit;
	}
} );

/* =========================================================================
 * GIAO DIỆN ON-PAGE CHECKLIST — dùng lại làm nội dung tab "On-page Optimization"
 * trong "Công việc SEO" (ttseo_render_work() ở workflow.php). Không tự vẽ
 * wrap/brand header/footer/styles — trang gọi hàm này lo phần khung.
 * ========================================================================= */
function ttseo_onpage_checklist_ui() {
	global $wpdb;
	$table = ttseo_op_table();
	$url   = isset( $_GET['u'] ) ? esc_url_raw( wp_unslash( $_GET['u'] ) ) : '';

	echo '<p class="ttseo-subtle" style="margin-left:0;">' . esc_html( ttseo_t(
		'Tự động tải URL và chấm 11 tiêu chí: tiêu đề, mô tả, heading, liên kết nội bộ, ảnh, dữ liệu có cấu trúc, CTA… Mỗi lần chạy lưu 1 snapshot để so sánh trước/sau và tự ghi vào chỉ tiêu tháng ở trên.',
		'Automatically fetches a URL and scores 11 criteria: title, description, headings, internal links, images, structured data, CTA… Each run saves a snapshot for a before/after comparison and logs it into the monthly targets above.'
	) ) . '</p>';
	$err = get_transient( 'ttseo_op_err' );
	if ( $err ) { echo '<div class="notice notice-error"><p>' . esc_html( $err ) . '</p></div>'; delete_transient( 'ttseo_op_err' ); }

	// Form chạy
	echo '<form method="post" style="background:#fff;border:1px solid #dcdfe3;border-radius:8px;padding:14px 16px;margin:12px 0;display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;">';
	wp_nonce_field( 'ttseo_op_run' );
	echo '<label style="flex:1;min-width:320px;">' . esc_html( ttseo_t( 'URL cần kiểm tra', 'URL to check' ) ) . '<br><input type="url" name="op_url" class="large-text" value="' . esc_attr( $url ) . '" required placeholder="' . esc_attr( home_url( '/product-example/' ) ) . '" list="ttseo-urls"></label>';
	echo '<datalist id="ttseo-urls">';
	$posts = get_posts( array( 'post_type' => array( 'page', 'product', 'post', 'project' ), 'post_status' => 'publish', 'numberposts' => 80, 'fields' => 'ids' ) );
	foreach ( $posts as $pid ) { echo '<option value="' . esc_attr( get_permalink( $pid ) ) . '">' . esc_attr( get_the_title( $pid ) ) . '</option>'; }
	echo '</datalist>';
	echo '<button class="button button-primary" name="ttseo_op_run" value="1">🔍 ' . esc_html( ttseo_t( 'Chấm điểm on-page', 'Run on-page score' ) ) . '</button>';
	echo '</form>';

	// Kết quả URL đang chọn
	if ( $url ) {
		$snaps = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table WHERE url=%s ORDER BY created_at ASC", $url ) );
		if ( $snaps ) {
			$first = $snaps[0];
			$last  = end( $snaps );
			$delta = $last->score - $first->score;

			echo '<h2 class="ttseo-sec" style="--b:#1f5c93;">' . esc_html( ttseo_t( 'Kết quả:', 'Result:' ) ) . ' <span style="font-weight:400;font-size:14px;">' . esc_html( str_replace( home_url(), '', $url ) ) . '</span></h2>';
			echo '<div class="ttseo-strip">';
			ttseo_kpi( ttseo_t( 'Lần đầu (TRƯỚC)', 'First run (BEFORE)' ), $first->score . '/100', mysql2date( 'd/m/Y H:i', $first->created_at ), '#8a8f94' );
			ttseo_kpi( ttseo_t( 'Mới nhất (SAU)', 'Latest (AFTER)' ), $last->score . '/100', mysql2date( 'd/m/Y H:i', $last->created_at ), ttseo_score_col( $last->score ) );
			ttseo_kpi( ttseo_t( 'Thay đổi', 'Change' ), ( $delta >= 0 ? '▲ +' : '▼ ' ) . $delta, count( $snaps ) . ttseo_t( ' lần kiểm tra', ' check(s)' ), $delta > 0 ? '#1a7f5a' : ( $delta < 0 ? '#c0392b' : '#8a8f94' ) );
			ttseo_kpi( ttseo_t( 'Tiêu chí đạt', 'Criteria passed' ), $last->passed . '/' . $last->total, '', ttseo_score_col( $last->score ) );
			echo '</div>';

			// Checklist chi tiết (lần mới nhất)
			$items = json_decode( $last->results, true );
			$fi    = json_decode( $first->results, true );
			$fmap  = array();
			// Khoá theo 'id' ổn định (không đổi theo ngôn ngữ); snapshot cũ trước bản song ngữ chưa có 'id' → tạm dùng 'k' làm dự phòng.
			foreach ( (array) $fi as $x ) { $fmap[ $x['id'] ?? $x['k'] ] = $x['ok']; }
			echo '<div class="ttseo-table"><table><thead><tr><th style="width:40px;">' . esc_html( ttseo_t( 'Trước', 'Before' ) ) . '</th><th style="width:40px;">' . esc_html( ttseo_t( 'Sau', 'After' ) ) . '</th><th>' . esc_html( ttseo_t( 'Tiêu chí', 'Criteria' ) ) . '</th><th>' . esc_html( ttseo_t( 'Hiện trạng', 'Status' ) ) . '</th><th>' . esc_html( ttseo_t( 'Chuẩn', 'Guideline' ) ) . '</th></tr></thead><tbody>';
			foreach ( (array) $items as $it ) {
				$was = $fmap[ $it['id'] ?? $it['k'] ] ?? null;
				echo '<tr>';
				echo '<td style="text-align:center;">' . ( null === $was ? '—' : ( $was ? '<span style="color:#1a7f5a;">✓</span>' : '<span style="color:#c0392b;">✗</span>' ) ) . '</td>';
				echo '<td style="text-align:center;font-size:15px;">' . ( $it['ok'] ? '<span style="color:#1a7f5a;">✓</span>' : '<span style="color:#c0392b;">✗</span>' ) . '</td>';
				echo '<td><b>' . esc_html( $it['k'] ) . '</b></td>';
				echo '<td style="font-size:12.5px;color:' . ( $it['ok'] ? '#50575e' : '#c0392b' ) . ';">' . esc_html( $it['v'] ) . '</td>';
				echo '<td style="font-size:11.5px;color:#8a8f94;">' . esc_html( $it['hint'] ) . '</td>';
				echo '</tr>';
			}
			echo '</tbody></table></div>';
		}
	}

	// Bảng tổng hợp các URL đã kiểm tra
	$all = $wpdb->get_results( "SELECT url, MAX(score) AS best, MIN(score) AS first_score, COUNT(*) AS runs, MAX(created_at) AS last_run FROM $table GROUP BY url ORDER BY last_run DESC LIMIT 60" );
	if ( $all ) {
		echo '<h2 class="ttseo-sec" style="--b:#c77d16;">' . esc_html( ttseo_t( 'Các URL đã kiểm tra', 'URLs checked' ) ) . '</h2>';
		echo '<div class="ttseo-table"><table><thead><tr><th>URL</th><th>' . esc_html( ttseo_t( 'Số lần', 'Runs' ) ) . '</th><th>' . esc_html( ttseo_t( 'Điểm cao nhất', 'Best score' ) ) . '</th><th>' . esc_html( ttseo_t( 'Lần cuối', 'Last run' ) ) . '</th><th></th></tr></thead><tbody>';
		foreach ( $all as $a ) {
			echo '<tr><td style="font-size:12.5px;">' . esc_html( str_replace( home_url(), '', $a->url ) ?: '/' ) . '</td>';
			echo '<td>' . (int) $a->runs . '</td>';
			echo '<td><span style="font-weight:700;color:' . esc_attr( ttseo_score_col( $a->best ) ) . ';">' . (int) $a->best . '/100</span></td>';
			echo '<td style="font-size:11.5px;color:#8a8f94;">' . esc_html( mysql2date( 'd/m/Y H:i', $a->last_run ) ) . '</td>';
			echo '<td><a href="' . esc_url( admin_url( 'admin.php?page=ttseo-work&tab=onpage&u=' . rawurlencode( $a->url ) ) ) . '" style="font-size:11.5px;">' . esc_html( ttseo_t( 'Xem chi tiết', 'View details' ) ) . '</a></td></tr>';
		}
		echo '</tbody></table></div>';
	}
}
