<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * @package Wisewand
 * @since 1.0.0
 * @license GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\Utils;

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use WP_Error;
use Wisewand\Logging\Logger;

// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names derived from $wpdb->prefix are safe to interpolate

class Competitor_Spy {

	private $repository;
	private $options;
	private $logger;
	private $dataforseo_client;
	private $user_domain;

	public function __construct( Options $options, Logger $logger, DataForSEO_Universal $dataforseo_client, $user_domain, \Wisewand\Repositories\Competitor_Repository $repository ) {
		$this->options           = $options;
		$this->logger            = $logger;
		$this->dataforseo_client = $dataforseo_client;
		$this->user_domain       = $this->sanitize_domain( $user_domain );
		$this->repository        = $repository;
	}

	/**
	 * Main entry point: Analyze competitors and return top keywords with progress tracking
	 *
	 * @param array  $competitor_domains Array of competitor domains (1-3)
	 * @param string $analysis_type 'gap', 'best_performers', or 'hybrid'
	 * @param array  $filters Optional filters (min_volume, max_difficulty, etc.)
	 * @param string $language_code Language code (e.g., 'en')
	 * @param int    $location_code Location code (e.g., 2840 for US)
	 * @return array|WP_Error Analysis results with top keywords and progress logs
	 */
	public function analyze_competitors( $competitor_domains, $analysis_type = 'hybrid', $filters = array(), $language_code = 'en', $location_code = 2840 ) {
		$analysis_id    = $this->generate_analysis_id();
		$progress_steps = array();

		$this->logger->info(
			'Starting competitor analysis',
			array(
				'analysis_id'   => $analysis_id,
				'user_domain'   => $this->user_domain,
				'competitors'   => $competitor_domains,
				'analysis_type' => $analysis_type,
			)
		);

		// Validate inputs
		$validation = $this->validate_analysis_request( $competitor_domains, $analysis_type );
		if ( is_wp_error( $validation ) ) {
			return $validation;
		}

		$filters = $this->normalize_filters( $filters );

		// Step 1: Fetch user's current rankings
		$progress_steps[] = $this->create_progress_step( 'fetch_user_rankings', 'started', "Fetching your domain rankings: {$this->user_domain}", '/dataforseo_labs/google/ranked_keywords/live' );

		$user_rankings = $this->fetch_user_rankings( $language_code, $location_code );

		$progress_steps[] = $this->create_progress_step( 'fetch_user_rankings', 'success', 'Found ' . count( $user_rankings ) . ' keywords you currently rank for' );

		$all_keywords      = array();
		$total_found       = 0;
		$total_competitors = count( $competitor_domains );

		// Step 2: Process each competitor
		foreach ( $competitor_domains as $index => $competitor ) {
			$competitor = $this->sanitize_domain( $competitor );
			$comp_num   = $index + 1;

			$progress_steps[] = $this->create_progress_step( 'analyze_competitor', 'started', "Analyzing competitor {$comp_num}/{$total_competitors}: {$competitor}" );

			$competitor_data = $this->analyze_single_competitor_with_progress(
				$competitor,
				$user_rankings,
				$analysis_type,
				$filters,
				$language_code,
				$location_code,
				$comp_num,
				$total_competitors
			);

			if ( ! is_wp_error( $competitor_data ) ) {
				$all_keywords   = array_merge( $all_keywords, $competitor_data['keywords'] );
				$total_found   += $competitor_data['total_found'];
				$progress_steps = array_merge( $progress_steps, $competitor_data['steps'] );

				$progress_steps[] = $this->create_progress_step( 'analyze_competitor', 'success', "✓ {$competitor}: Found {$competitor_data['total_found']} keywords, kept " . count( $competitor_data['keywords'] ) . ' after filtering' );
			} else {
				$progress_steps[] = $this->create_progress_step( 'analyze_competitor', 'error', "✗ {$competitor}: " . $competitor_data->get_error_message() );
			}
		}

		// Step 3: Deduplicate and rank
		$progress_steps[] = $this->create_progress_step( 'merge_results', 'started', "Merging results from {$total_competitors} competitors..." );

		$ranked_keywords = $this->rank_and_deduplicate_keywords( $all_keywords, $filters['max_results'] );

		$progress_steps[] = $this->create_progress_step( 'merge_results', 'success', "✓ Deduplicated and ranked top {$filters['max_results']} keywords" );

		// Step 4: Save to database
		$progress_steps[] = $this->create_progress_step( 'save_analysis', 'started', 'Saving analysis to database...' );

		$this->save_analysis( $analysis_id, $competitor_domains, $ranked_keywords, $analysis_type, $filters, $language_code, $location_code, $total_found );

		$progress_steps[] = $this->create_progress_step( 'save_analysis', 'success', '✓ Analysis saved successfully' );

		return array(
			'analysis_id'          => $analysis_id,
			'keywords'             => $ranked_keywords,
			'total_found'          => $total_found,
			'total_returned'       => count( $ranked_keywords ),
			'competitors_analyzed' => count( $competitor_domains ),
			'filters_applied'      => $filters,
			'progress_steps'       => $progress_steps,
		);
	}

	private function create_progress_step( $step_type, $status, $message, $endpoint = null, $count = null ) {
		return array(
			'step'      => $step_type,
			'status'    => $status,
			'message'   => $message,
			'endpoint'  => $endpoint,
			'count'     => $count,
			'timestamp' => current_time( 'mysql' ),
		);
	}

	/**
	 * Analyze a single competitor with progress tracking
	 */
	private function analyze_single_competitor_with_progress( $competitor, $user_rankings, $analysis_type, $filters, $language_code, $location_code, $comp_num, $total_competitors ) {
		$steps = array();

		return $this->analyze_single_competitor( $competitor, $user_rankings, $analysis_type, $filters, $language_code, $location_code, $steps );
	}

	/**
	 * Analyze a single competitor (core logic)
	 */
	private function analyze_single_competitor( $competitor, $user_rankings, $analysis_type, $filters, $language_code, $location_code, &$steps = array() ) {
		$keywords = array();

		// Step 1: Fetch competitor's ranked keywords
		if ( in_array( $analysis_type, array( 'best_performers', 'hybrid' ) ) ) {
			$steps[] = $this->create_progress_step( 'fetch_ranked', 'started', "Fetching ranked keywords for {$competitor}", '/dataforseo_labs/google/ranked_keywords/live' );

			$ranked_keywords = $this->fetch_competitor_ranked_keywords( $competitor, $filters, $language_code, $location_code );

			if ( ! is_wp_error( $ranked_keywords ) ) {
				$keywords = array_merge( $keywords, $ranked_keywords );
				$steps[]  = $this->create_progress_step( 'fetch_ranked', 'success', '✓ Found ' . count( $ranked_keywords ) . ' ranked keywords', null, count( $ranked_keywords ) );

				// Fallback: If 0 results, try keywords_for_site (broader discovery)
				if ( empty( $ranked_keywords ) ) {
					$steps[] = $this->create_progress_step( 'fetch_site_keywords', 'started', 'Trying alternative: keywords_for_site endpoint (broader discovery)', '/dataforseo_labs/google/keywords_for_site/live' );

					$site_keywords = $this->fetch_keywords_for_site( $competitor, $language_code, $location_code );

					if ( ! is_wp_error( $site_keywords ) && ! empty( $site_keywords ) ) {
						$keywords = array_merge( $keywords, $site_keywords );
						$steps[]  = $this->create_progress_step( 'fetch_site_keywords', 'success', '✓ Found ' . count( $site_keywords ) . ' keywords via keywords_for_site', null, count( $site_keywords ) );
					} else {
						$steps[] = $this->create_progress_step( 'fetch_site_keywords', 'warning', '⚠ No keywords found. Domain might not be in DataForSEO database.' );
					}
				}
			} else {
				$steps[] = $this->create_progress_step( 'fetch_ranked', 'error', '✗ Failed: ' . $ranked_keywords->get_error_message() );
			}
		}

		// Step 2: Domain gap analysis
		if ( in_array( $analysis_type, array( 'gap', 'hybrid' ) ) ) {
			$steps[] = $this->create_progress_step( 'fetch_gap', 'started', "Running gap analysis (keywords they rank for, you don't)", '/dataforseo_labs/google/domain_intersection/live' );

			$gap_keywords = $this->fetch_domain_gap( $competitor, $filters, $language_code, $location_code );

			if ( ! is_wp_error( $gap_keywords ) ) {
				$keywords = array_merge( $keywords, $gap_keywords );
				$steps[]  = $this->create_progress_step( 'fetch_gap', 'success', '✓ Found ' . count( $gap_keywords ) . ' keyword gaps', null, count( $gap_keywords ) );
			} else {
				$steps[] = $this->create_progress_step( 'fetch_gap', 'error', '✗ Failed: ' . $gap_keywords->get_error_message() );
			}
		}

		$total_keywords = count( $keywords );

		// Step 3: Calculate relevance scores
		$steps[] = $this->create_progress_step( 'calculate_relevance', 'started', "Calculating relevance scores for {$total_keywords} keywords..." );

		$keywords_with_relevance = $this->calculate_relevance_scores( $keywords, $user_rankings );

		$steps[] = $this->create_progress_step( 'calculate_relevance', 'success', '✓ Relevance scores calculated' );

		// Step 4: Apply filters and scoring
		$steps[] = $this->create_progress_step( 'filter_score', 'started', 'Applying filters and calculating priority scores...' );

		$filtered_keywords = $this->apply_filters_and_score( $keywords_with_relevance, $filters, $competitor );

		$steps[] = $this->create_progress_step( 'filter_score', 'success', '✓ Kept ' . count( $filtered_keywords ) . ' keywords after filtering', null, count( $filtered_keywords ) );

		return array(
			'competitor'  => $competitor,
			'keywords'    => $filtered_keywords,
			'total_found' => $total_keywords,
			'steps'       => $steps,
		);
	}

	/**
	 * Fetch competitor's top-ranking keywords
	 */
	private function fetch_competitor_ranked_keywords( $competitor, $filters, $language_code, $location_code ) {
		$this->logger->info(
			'Fetching ranked keywords',
			array(
				'competitor' => $competitor,
				'language'   => $language_code,
				'location'   => $location_code,
				'limit'      => 500,
			)
		);

		// Build filters - but don't send to DataForSEO, filter results after fetching
		// DataForSEO's filters API is strict and can cause "Invalid Field" errors
		$payload = array(
			array(
				'target'             => $competitor,
				'language_code'      => $language_code,
				'location_code'      => $location_code,
				'limit'              => 500,
				'load_rank_absolute' => true,
				'item_types'         => array( 'organic' ),
				'include_serp_info'  => true,
				'order_by'           => array( 'ranked_serp_element.serp_item.rank_group,asc' ),
			),
		);

		// NOTE: Not using DataForSEO filters to avoid API errors
		// We filter results in PHP after fetching (more flexible and reliable)

		$this->logger->info(
			'DataForSEO ranked_keywords request',
			array(
				'competitor'          => $competitor,
				'language_code'       => $language_code,
				'location_code'       => $location_code,
				'limit'               => 500,
				'php_filters_enabled' => true,
			)
		);

		$this->logger->debug( 'DataForSEO ranked_keywords payload', array( 'payload' => $payload ) );

		$response = $this->dataforseo_client->request( 'POST', '/dataforseo_labs/google/ranked_keywords/live', $payload );

		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Ranked keywords API error',
				array(
					'competitor' => $competitor,
					'error'      => $response->get_error_message(),
				)
			);
			return $response;
		}

		$this->logger->debug(
			'DataForSEO ranked_keywords response',
			array(
				'competitor'         => $competitor,
				'response_structure' => array(
					'tasks'              => count( $response ),
					'first_task_status'  => $response[0]['status_code'] ?? 'unknown',
					'first_task_message' => $response[0]['status_message'] ?? 'no message',
					'result_count'       => isset( $response[0]['result'][0]['items'] ) ? count( $response[0]['result'][0]['items'] ) : 0,
				),
			)
		);

		return $this->parse_ranked_keywords_response( $response, $competitor );
	}

	/**
	 * Fetch keywords for site (fallback when ranked_keywords returns 0)
	 */
	private function fetch_keywords_for_site( $competitor, $language_code, $location_code ) {
		$this->logger->info(
			'Fetching keywords_for_site (fallback)',
			array(
				'competitor' => $competitor,
				'language'   => $language_code,
				'location'   => $location_code,
			)
		);

		$payload = array(
			array(
				'target'             => $competitor,
				'language_code'      => $language_code,
				'location_code'      => $location_code,
				'limit'              => 100, // Smaller limit for fallback
				'include_serp_info'  => false,
				'include_subdomains' => true,
			),
		);

		$response = $this->dataforseo_client->get_keywords_for_site( $competitor, $language_code, $location_code, 100, true );

		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Keywords for site API error',
				array(
					'competitor' => $competitor,
					'error'      => $response->get_error_message(),
				)
			);
			return $response;
		}

		// Parse response
		$keywords = array();
		$task     = $response[0] ?? array();
		$items    = $task['result'][0]['items'] ?? array();

		foreach ( $items as $item ) {
			$keyword_data = $item['keyword_data'] ?? array();
			$keyword      = $keyword_data['keyword'] ?? '';

			if ( empty( $keyword ) ) {
				continue;
			}

			$keywords[] = array(
				'keyword'             => $keyword,
				'search_volume'       => $keyword_data['keyword_info']['search_volume'] ?? 0,
				'difficulty'          => $keyword_data['keyword_properties']['keyword_difficulty'] ?? null,
				'competitor_position' => null, // Not available in keywords_for_site
				'user_position'       => null,
				'gap_type'            => 'site_relevance',
				'search_intent'       => $this->detect_search_intent( $keyword ),
				'serp_data'           => array(),
			);
		}

		$this->logger->info(
			'Parsed keywords_for_site',
			array(
				'competitor' => $competitor,
				'total'      => count( $keywords ),
			)
		);

		return $keywords;
	}

	/**
	 * Fetch keyword gaps (competitor ranks, user doesn't)
	 */
	private function fetch_domain_gap( $competitor, $filters, $language_code, $location_code ) {
		$this->logger->info(
			'Fetching domain gap',
			array(
				'competitor'  => $competitor,
				'user_domain' => $this->user_domain,
				'language'    => $language_code,
				'location'    => $location_code,
			)
		);

		$payload = array(
			array(
				'target1'           => $competitor,
				'target2'           => $this->user_domain,
				'language_code'     => $language_code,
				'location_code'     => $location_code,
				'intersections'     => false, // Keywords target1 ranks for, target2 doesn't
				'limit'             => 500,
				'include_serp_info' => true,
			),
		);

		// NOTE: Filtering done in PHP after fetching to avoid DataForSEO API errors

		$this->logger->debug( 'DataForSEO domain_intersection payload', array( 'payload' => $payload ) );

		$response = $this->dataforseo_client->request( 'POST', '/dataforseo_labs/google/domain_intersection/live', $payload );

		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Domain intersection API error',
				array(
					'competitor' => $competitor,
					'error'      => $response->get_error_message(),
				)
			);
			return $response;
		}

		$this->logger->debug(
			'DataForSEO domain_intersection response',
			array(
				'competitor'         => $competitor,
				'response_structure' => array(
					'tasks'              => count( $response ),
					'first_task_status'  => $response[0]['status_code'] ?? 'unknown',
					'first_task_message' => $response[0]['status_message'] ?? 'no message',
					'result_count'       => isset( $response[0]['result'][0]['items'] ) ? count( $response[0]['result'][0]['items'] ) : 0,
				),
			)
		);

		return $this->parse_domain_intersection_response( $response, $competitor );
	}

	/**
	 * Fetch user's current rankings for comparison
	 */
	private function fetch_user_rankings( $language_code, $location_code ) {
		$this->logger->debug( 'Fetching user domain rankings', array( 'domain' => $this->user_domain ) );

		$payload = array(
			array(
				'target'        => $this->user_domain,
				'language_code' => $language_code,
				'location_code' => $location_code,
				'limit'         => 1000,
				'item_types'    => array( 'organic' ),
			),
		);

		$response = $this->dataforseo_client->request( 'POST', '/dataforseo_labs/google/ranked_keywords/live', $payload );

		if ( is_wp_error( $response ) ) {
			$this->logger->warning(
				'Could not fetch user rankings',
				array(
					'error' => $response->get_error_message(),
				)
			);
			return array();
		}

		$rankings = array();
		$task     = $response[0] ?? array();
		$items    = $task['result'][0]['items'] ?? array();

		foreach ( $items as $item ) {
			$keyword  = $item['keyword_data']['keyword'] ?? '';
			$position = $item['ranked_serp_element']['serp_item']['rank_absolute'] ?? 999;

			if ( $keyword ) {
				$rankings[ strtolower( $keyword ) ] = $position;
			}
		}

		$this->logger->info( 'User rankings fetched', array( 'total' => count( $rankings ) ) );

		return $rankings;
	}

	/**
	 * Smart relevance scoring algorithm
	 */
	private function calculate_relevance_scores( $keywords, $user_rankings ) {
		// Build user's keyword topic model
		$user_topics = $this->extract_topics_from_keywords( array_keys( $user_rankings ) );

		foreach ( $keywords as &$kw ) {
			$kw['relevance_score'] = $this->calculate_keyword_relevance(
				$kw['keyword'],
				$user_topics,
				$user_rankings
			);
		}

		return $keywords;
	}

	/**
	 * Calculate single keyword relevance (0-100)
	 */
	private function calculate_keyword_relevance( $keyword, $user_topics, $user_rankings ) {
		$score = 0;

		// 1. Topic Similarity (0-40 points)
		$topic_score = $this->calculate_topic_similarity( $keyword, $user_topics );
		$score      += $topic_score * 40;

		// 2. Search Intent Match (0-30 points)
		$intent = $this->detect_search_intent( $keyword );
		if ( in_array( $intent, array( 'informational', 'commercial' ) ) ) {
			$score += 30;
		} elseif ( $intent === 'transactional' ) {
			$score += 20;
		}

		// 3. Keyword Structure Quality (0-20 points)
		$word_count = count( explode( ' ', $keyword ) );
		if ( $word_count >= 3 && $word_count <= 5 ) { // Long-tail sweet spot
			$score += 20;
		} elseif ( $word_count >= 2 ) {
			$score += 10;
		}

		// 4. Semantic Proximity (0-10 points)
		$proximity = $this->calculate_semantic_proximity( $keyword, array_keys( $user_rankings ) );
		$score    += $proximity * 10;

		return min( 100, round( $score, 2 ) );
	}

	/**
	 * Calculate topic similarity using TF-IDF-like approach
	 */
	private function calculate_topic_similarity( $keyword, $user_topics ) {
		$keyword_words = $this->extract_significant_words( $keyword );
		$matches       = 0;
		$total         = count( $keyword_words );

		if ( $total === 0 ) {
			return 0;
		}

		foreach ( $keyword_words as $word ) {
			if ( isset( $user_topics[ $word ] ) ) {
				$matches += $user_topics[ $word ]; // Weighted by frequency
			}
		}

		return min( 1, $matches / $total );
	}

	/**
	 * Extract significant words and build topic model
	 */
	private function extract_topics_from_keywords( $keywords ) {
		$topics    = array();
		$stopwords = array( 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'how', 'what', 'best', 'top' );

		foreach ( $keywords as $keyword ) {
			$words = $this->extract_significant_words( $keyword );

			foreach ( $words as $word ) {
				if ( ! in_array( $word, $stopwords ) && strlen( $word ) > 2 ) {
					$topics[ $word ] = ( $topics[ $word ] ?? 0 ) + 1;
				}
			}
		}

		// Normalize by max frequency
		$max_freq = max( array_values( $topics ) ?: array( 1 ) );
		foreach ( $topics as $word => $freq ) {
			$topics[ $word ] = $freq / $max_freq;
		}

		return $topics;
	}

	/**
	 * Extract significant words from keyword
	 */
	private function extract_significant_words( $keyword ) {
		$words = preg_split( '/[\s\-_]+/', strtolower( $keyword ) );
		return array_filter(
			$words,
			function ( $word ) {
				return strlen( $word ) > 2 && ! is_numeric( $word );
			}
		);
	}

	/**
	 * Calculate semantic proximity (basic Jaccard similarity)
	 */
	private function calculate_semantic_proximity( $keyword, $user_keywords ) {
		$kw_words       = array_unique( $this->extract_significant_words( $keyword ) );
		$max_similarity = 0;

		// Compare with top 100 user keywords for performance
		$sample_user_keywords = array_slice( $user_keywords, 0, 100 );

		foreach ( $sample_user_keywords as $user_kw ) {
			$user_words = array_unique( $this->extract_significant_words( $user_kw ) );

			$intersection = count( array_intersect( $kw_words, $user_words ) );
			$union        = count( array_unique( array_merge( $kw_words, $user_words ) ) );

			if ( $union > 0 ) {
				$similarity     = $intersection / $union;
				$max_similarity = max( $max_similarity, $similarity );
			}
		}

		return $max_similarity;
	}

	/**
	 * Detect search intent from keyword
	 */
	private function detect_search_intent( $keyword ) {
		$keyword_lower = strtolower( $keyword );

		// Informational signals
		$informational = array( 'how', 'what', 'why', 'when', 'where', 'who', 'guide', 'tutorial', 'learn', 'tips' );
		foreach ( $informational as $signal ) {
			if ( strpos( $keyword_lower, $signal ) !== false ) {
				return 'informational';
			}
		}

		// Transactional signals
		$transactional = array( 'buy', 'price', 'cost', 'cheap', 'deal', 'discount', 'coupon', 'order', 'purchase' );
		foreach ( $transactional as $signal ) {
			if ( strpos( $keyword_lower, $signal ) !== false ) {
				return 'transactional';
			}
		}

		// Commercial investigation
		$commercial = array( 'best', 'top', 'review', 'vs', 'versus', 'compare', 'alternative' );
		foreach ( $commercial as $signal ) {
			if ( strpos( $keyword_lower, $signal ) !== false ) {
				return 'commercial';
			}
		}

		// Navigational (usually branded)
		if ( strpos( $keyword_lower, $this->user_domain ) !== false ) {
			return 'navigational';
		}

		return 'informational'; // Default
	}

	/**
	 * Apply filters and calculate final priority score
	 */
	private function apply_filters_and_score( $keywords, $filters, $competitor_domain ) {
		$filtered = array();

		foreach ( $keywords as $kw ) {
			// Apply volume filter
			if ( $kw['search_volume'] < $filters['min_volume'] ) {
				continue;
			}

			// Apply difficulty filter
			if ( $kw['difficulty'] !== null && $kw['difficulty'] > $filters['max_difficulty'] ) {
				continue;
			}

			// Apply position filter (for competitor)
			if ( isset( $kw['competitor_position'] ) ) {
				if ( $kw['competitor_position'] < $filters['position_min'] || $kw['competitor_position'] > $filters['position_max'] ) {
					continue;
				}
			}

			// Exclude branded terms if enabled
			if ( $filters['exclude_branded'] ) {
				$competitor_name = $this->extract_brand_name( $competitor_domain );
				if ( stripos( $kw['keyword'], $competitor_name ) !== false ) {
					continue;
				}
			}

			// Exclude navigational intent if configured
			if ( $kw['search_intent'] === 'navigational' ) {
				continue;
			}

			// Calculate final priority score
			$kw['priority_score']    = $this->calculate_priority_score( $kw );
			$kw['competitor_domain'] = $competitor_domain;

			$filtered[] = $kw;
		}

		return $filtered;
	}

	/**
	 * Calculate priority score (0-100)
	 */
	private function calculate_priority_score( $kw ) {
		$volume_score     = min( 40, ( $kw['search_volume'] / 1000 ) * 10 ); // Max 40 points
		$relevance_score  = ( $kw['relevance_score'] ?? 50 ) * 0.3; // Max 30 points
		$difficulty_score = ( 100 - ( $kw['difficulty'] ?? 50 ) ) * 0.2; // Max 20 points, inverted

		// Position bonus
		$position_bonus = 0;
		if ( isset( $kw['competitor_position'] ) ) {
			if ( $kw['competitor_position'] <= 3 ) {
				$position_bonus = 10;
			} elseif ( $kw['competitor_position'] <= 10 ) {
				$position_bonus = 5;
			} elseif ( $kw['competitor_position'] <= 20 ) {
				$position_bonus = 2;
			}
		}

		return min( 100, round( $volume_score + $relevance_score + $difficulty_score + $position_bonus, 2 ) );
	}

	/**
	 * Rank and deduplicate keywords
	 */
	private function rank_and_deduplicate_keywords( $keywords, $max_results = 200 ) {
		// Remove exact duplicates, keep highest priority
		$deduplicated = array();

		foreach ( $keywords as $kw ) {
			$key = strtolower( $kw['keyword'] );

			if ( ! isset( $deduplicated[ $key ] ) || $kw['priority_score'] > $deduplicated[ $key ]['priority_score'] ) {
				$deduplicated[ $key ] = $kw;
			}
		}

		// Sort by priority score
		usort(
			$deduplicated,
			function ( $a, $b ) {
				return $b['priority_score'] <=> $a['priority_score'];
			}
		);

		// Return top N results
		return array_slice( $deduplicated, 0, $max_results );
	}

	/**
	 * Parse DataForSEO ranked_keywords response
	 */
	private function parse_ranked_keywords_response( $response, $competitor ) {
		$keywords = array();
		$task     = $response[0] ?? array();
		$items    = $task['result'][0]['items'] ?? array();

		// Check for API errors
		if ( isset( $task['status_code'] ) && $task['status_code'] !== 20000 ) {
			$error_message = $task['status_message'] ?? 'Unknown API error';
			$this->logger->error(
				'DataForSEO API error',
				array(
					'competitor'  => $competitor,
					'status_code' => $task['status_code'],
					'message'     => $error_message,
				)
			);
			return new WP_Error( 'dataforseo_api_error', $error_message );
		}

		foreach ( $items as $item ) {
			$keyword_data = $item['keyword_data'] ?? array();
			$serp_element = $item['ranked_serp_element'] ?? array();
			$serp_item    = $serp_element['serp_item'] ?? array();

			$keyword = $keyword_data['keyword'] ?? '';
			if ( empty( $keyword ) ) {
				continue;
			}

			// Extract difficulty from multiple possible sources
			$difficulty = null;
			if ( isset( $keyword_data['keyword_properties']['keyword_difficulty'] ) ) {
				$difficulty = $keyword_data['keyword_properties']['keyword_difficulty'];
			} elseif ( isset( $keyword_data['keyword_info']['competition_index'] ) ) {
				$difficulty = $keyword_data['keyword_info']['competition_index'];
			} elseif ( isset( $keyword_data['keyword_info']['competition'] ) ) {
				$comp_map   = array(
					'LOW'    => 25,
					'MEDIUM' => 50,
					'HIGH'   => 75,
				);
				$difficulty = $comp_map[ strtoupper( $keyword_data['keyword_info']['competition'] ) ] ?? null;
			}

			// Extract CPC
			$cpc = null;
			if ( isset( $keyword_data['keyword_info']['cpc'] ) && is_numeric( $keyword_data['keyword_info']['cpc'] ) ) {
				$cpc = round( (float) $keyword_data['keyword_info']['cpc'], 2 );
			}

			// Extract Competition
			$competition = null;
			if ( isset( $keyword_data['keyword_info']['competition'] ) && is_string( $keyword_data['keyword_info']['competition'] ) ) {
				$competition = strtoupper( $keyword_data['keyword_info']['competition'] );
			}

			$keywords[] = array(
				'keyword'             => $keyword,
				'search_volume'       => $keyword_data['keyword_info']['search_volume'] ?? 0,
				'difficulty'          => $difficulty,
				'cpc'                 => $cpc,
				'competition'         => $competition,
				'competitor_position' => $serp_item['rank_absolute'] ?? null,
				'user_position'       => null,
				'gap_type'            => null,
				'search_intent'       => $this->detect_search_intent( $keyword ),
				'serp_data'           => array(
					'etv'             => $serp_element['etv'] ?? 0,
					'impressions_etv' => $serp_element['impressions_etv'] ?? 0,
					'url'             => $serp_item['url'] ?? '',
				),
			);
		}

		if ( empty( $keywords ) ) {
			$this->logger->warning(
				'No ranked keywords found for competitor',
				array(
					'competitor'         => $competitor,
					'api_response_items' => count( $items ),
					'task_status'        => $task['status_code'] ?? 'unknown',
				)
			);
		}

		$this->logger->info(
			'Parsed ranked keywords',
			array(
				'competitor' => $competitor,
				'total'      => count( $keywords ),
			)
		);

		return $keywords;
	}

	/**
	 * Parse DataForSEO domain_intersection response
	 */
	private function parse_domain_intersection_response( $response, $competitor ) {
		$keywords = array();
		$task     = $response[0] ?? array();
		$items    = $task['result'][0]['items'] ?? array();

		foreach ( $items as $item ) {
			$keyword_data      = $item['keyword_data'] ?? array();
			$first_domain_serp = $item['first_domain_serp_element'] ?? array();
			$first_serp_item   = $first_domain_serp['serp_item'] ?? array();

			$keyword = $keyword_data['keyword'] ?? '';
			if ( empty( $keyword ) ) {
				continue;
			}

			// Extract difficulty from multiple sources
			$difficulty = null;
			if ( isset( $keyword_data['keyword_properties']['keyword_difficulty'] ) ) {
				$difficulty = $keyword_data['keyword_properties']['keyword_difficulty'];
			} elseif ( isset( $keyword_data['keyword_info']['competition_index'] ) ) {
				$difficulty = $keyword_data['keyword_info']['competition_index'];
			} elseif ( isset( $keyword_data['keyword_info']['competition'] ) ) {
				$comp_map   = array(
					'LOW'    => 25,
					'MEDIUM' => 50,
					'HIGH'   => 75,
				);
				$difficulty = $comp_map[ strtoupper( $keyword_data['keyword_info']['competition'] ) ] ?? null;
			}

			// Extract CPC
			$cpc = null;
			if ( isset( $keyword_data['keyword_info']['cpc'] ) && is_numeric( $keyword_data['keyword_info']['cpc'] ) ) {
				$cpc = round( (float) $keyword_data['keyword_info']['cpc'], 2 );
			}

			// Extract Competition
			$competition = null;
			if ( isset( $keyword_data['keyword_info']['competition'] ) && is_string( $keyword_data['keyword_info']['competition'] ) ) {
				$competition = strtoupper( $keyword_data['keyword_info']['competition'] );
			}

			$keywords[] = array(
				'keyword'             => $keyword,
				'search_volume'       => $keyword_data['keyword_info']['search_volume'] ?? 0,
				'difficulty'          => $difficulty,
				'cpc'                 => $cpc,
				'competition'         => $competition,
				'competitor_position' => $first_serp_item['rank_absolute'] ?? null,
				'user_position'       => null, // User doesn't rank (that's the gap)
				'gap_type'            => 'missing',
				'search_intent'       => $this->detect_search_intent( $keyword ),
				'serp_data'           => array(
					'etv' => $first_domain_serp['etv'] ?? 0,
					'url' => $first_serp_item['url'] ?? '',
				),
			);
		}

		$this->logger->info(
			'Parsed domain gap keywords',
			array(
				'competitor' => $competitor,
				'gaps_found' => count( $keywords ),
			)
		);

		return $keywords;
	}

	/**
	 * Build DataForSEO filters array
	 */
	private function build_dataforseo_filters( $filters, $endpoint_type ) {
		$dataforseo_filters = array();

		// Volume filter
		if ( $filters['min_volume'] > 0 ) {
			$dataforseo_filters[] = array( 'keyword_data.keyword_info.search_volume', '>=', $filters['min_volume'] );
		}

		// Difficulty filter
		if ( $filters['max_difficulty'] < 100 ) {
			$dataforseo_filters[] = array( 'keyword_data.keyword_properties.keyword_difficulty', '<=', $filters['max_difficulty'] );
		}

		// Position filters (ranked_keywords only)
		if ( 'ranked' === $endpoint_type && $filters['position_max'] > 0 ) {
			$dataforseo_filters[] = array( 'ranked_serp_element.serp_item.rank_absolute', '<=', $filters['position_max'] );
		}

		return $dataforseo_filters;
	}

	/**
	 * Normalize filters with smart defaults
	 */
	private function normalize_filters( $filters ) {
		return array_merge(
			array(
				'min_volume'      => 100,
				'max_difficulty'  => 50,
				'position_min'    => 1,
				'position_max'    => 50,
				'exclude_branded' => true,
				'max_results'     => 200,
			),
			$filters
		);
	}

	/**
	 * Save analysis to database
	 */
	/**
	 * Save analysis to database
	 */
	private function save_analysis( $analysis_id, $competitor_domains, $keywords, $analysis_type, $filters, $language_code, $location_code, $total_found ) {
		$data = array(
			'analysis_id'          => $analysis_id,
			'user_domain'          => $this->user_domain,
			'competitor_domains'   => wp_json_encode( $competitor_domains ),
			'language_code'        => $language_code,
			'location_code'        => $location_code,
			'analysis_type'        => $analysis_type,
			'total_keywords_found' => $total_found,
			'keywords_saved'       => 0, // Updated when user saves keywords
			'filters_used'         => wp_json_encode( $filters ),
			'analysis_data'        => wp_json_encode(
				array(
					'top_keywords' => array_slice( $keywords, 0, 50 ), // Store top 50
					'timestamp'    => current_time( 'mysql' ),
				)
			),
			'created_by'           => get_current_user_id(),
		);

		$result = $this->repository->save_analysis( $data );

		if ( ! $result ) {
			$this->logger->error(
				'Failed to save analysis',
				array(
					'error' => $this->repository->get_last_error(),
				)
			);
		}

		// Save detailed keyword data
		$this->save_competitor_keywords( $analysis_id, $keywords );

		$this->logger->info(
			'Analysis saved to database',
			array(
				'analysis_id'    => $analysis_id,
				'keywords_count' => count( $keywords ),
			)
		);
	}

	/**
	 * Create competitor analysis tables if they don't exist
	 */


	/**
	 * Save competitor keywords to database
	 */
	/**
	 * Save competitor keywords to database
	 */
	private function save_competitor_keywords( $analysis_id, $keywords ) {
		$keywords_data = array();

		foreach ( $keywords as $kw ) {
			$keywords_data[] = array(
				'analysis_id'         => $analysis_id,
				'keyword'             => $kw['keyword'],
				'search_volume'       => $kw['search_volume'],
				'difficulty'          => $kw['difficulty'],
				'competitor_domain'   => $kw['competitor_domain'],
				'competitor_position' => $kw['competitor_position'],
				'user_position'       => $kw['user_position'],
				'gap_type'            => $kw['gap_type'],
				'relevance_score'     => $kw['relevance_score'] ?? 0,
				'priority_score'      => $kw['priority_score'] ?? 0,
				'serp_data'           => isset( $kw['serp_data'] ) ? wp_json_encode( $kw['serp_data'] ) : null,
			);
		}

		$this->repository->save_competitor_keywords( $keywords_data );
	}

	/**
	 * Auto-discover competitors for a domain
	 */
	public function discover_competitors( $language_code = 'en', $location_code = 2840, $limit = 5 ) {
		$this->logger->info( 'Auto-discovering competitors', array( 'domain' => $this->user_domain ) );

		// Strategy: Get domains ranking for user's top keywords
		$user_rankings = $this->fetch_user_rankings( $language_code, $location_code );

		if ( empty( $user_rankings ) ) {
			return new WP_Error( 'no_user_rankings', 'User domain has no rankings to analyze' );
		}

		// Get top 10 user keywords
		$top_keywords = array_slice( array_keys( $user_rankings ), 0, 10 );

		$competitor_frequency = array();

		// For each top keyword, find who else ranks
		foreach ( $top_keywords as $keyword ) {
			$serp_competitors = $this->fetch_serp_competitors( $keyword, $language_code, $location_code );

			foreach ( $serp_competitors as $domain ) {
				if ( $domain !== $this->user_domain ) {
					$competitor_frequency[ $domain ] = ( $competitor_frequency[ $domain ] ?? 0 ) + 1;
				}
			}
		}

		// Sort by frequency
		arsort( $competitor_frequency );

		$competitors = array_slice( array_keys( $competitor_frequency ), 0, $limit );

		$this->logger->info(
			'Competitors discovered',
			array(
				'total'           => count( $competitors ),
				'top_competitors' => $competitors,
			)
		);

		return $competitors;
	}

	/**
	 * Fetch competing domains for a specific keyword
	 */
	private function fetch_serp_competitors( $keyword, $language_code, $location_code ) {
		// Use ranked_keywords in reverse: find domains ranking for this keyword
		// For simplicity, we'll use a dedicated SERP endpoint if available
		// Otherwise, sample from domain_intersection with multiple known competitors

		$domains = array();

		// Simplified: return empty for now, implement full SERP crawl later
		// In production, use /serp/google/organic/live endpoint

		return $domains;
	}

	/**
	 * Validate analysis request
	 */
	private function validate_analysis_request( $competitor_domains, $analysis_type ) {
		if ( empty( $competitor_domains ) || ! is_array( $competitor_domains ) ) {
			return new WP_Error( 'invalid_competitors', 'Please provide at least one competitor domain' );
		}

		if ( count( $competitor_domains ) > 3 ) {
			return new WP_Error( 'too_many_competitors', 'Maximum 3 competitors allowed per analysis' );
		}

		$valid_types = array( 'gap', 'best_performers', 'hybrid' );
		if ( ! in_array( $analysis_type, $valid_types ) ) {
			return new WP_Error( 'invalid_analysis_type', 'Invalid analysis type' );
		}

		return true;
	}

	/**
	 * Utility: Sanitize domain
	 */
	private function sanitize_domain( $domain ) {
		$domain = strtolower( trim( $domain ) );
		$domain = preg_replace( '#^https?://(www\.)?#', '', $domain );
		$domain = rtrim( $domain, '/' );

		return $domain;
	}

	/**
	 * Utility: Extract brand name from domain
	 */
	private function extract_brand_name( $domain ) {
		$parts = explode( '.', $domain );
		return $parts[0] ?? $domain;
	}

	/**
	 * Utility: Generate unique analysis ID
	 */
	private function generate_analysis_id() {
		return 'comp_' . substr( md5( uniqid( $this->user_domain, true ) ), 0, 16 );
	}

	/**
	 * Get historical analyses for user's domain
	 */
	public function get_analysis_history( $limit = 10 ) {
		return $this->repository->get_analysis_history( $this->user_domain, $limit );
	}

	/**
	 * Get keywords from specific analysis
	 */
	public function get_analysis_keywords( $analysis_id ) {
		return $this->repository->get_analysis_keywords( $analysis_id );
	}
}
