<?php

if (!defined('WPINC')) {
	die;
}

// Includes
if (!class_exists('PagesController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/PagesController.php';
}
if (!class_exists('PostsController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/PostsController.php';
}
if (!class_exists('PostsCategoriesController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/PostsCategoriesController.php';
}
if (!class_exists('ProductCategoriesController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/ProductCategoriesController.php';
}
if (!class_exists('ProductAttributesController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/ProductAttributesController.php';
}
if (!class_exists('ProductsController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/ProductsController.php';
}
if (!class_exists('BrandsManufacturersController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/BrandsManufacturersController.php';
}
if (!class_exists('DocumentsController')) {
	require_once ASWP_ATHENA_CORE_INC . 'Controller/DocumentsController.php';
}
if (!function_exists('authorized_fetch')) {
	require_once __DIR__ . '/../Helpers/authorization.php';
}

class FullReindexController {

	public function calculateTotalItemsToSync() {
		global $wpdb;
		$total = 0;

		// Brands/Manufacturers
		$countBrands = 0;
		if (taxonomy_exists('product_brand')) {
			$countBrands = (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(t.term_id) FROM {$wpdb->terms} t
				INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
				WHERE tt.taxonomy = %s",
					'product_brand'
				)
			);
			$total += $countBrands;
		}

		// Documents
		$mime_types = [
			'application/pdf',
			'application/msword',
			'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
			'application/vnd.ms-excel',
			'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			'application/vnd.ms-powerpoint',
			'application/vnd.openxmlformats-officedocument.presentationml.presentation',
			'text/plain',
			'text/csv',
			'application/rtf',
		];
		$placeholders = implode(',', array_fill(0, count($mime_types), '%s'));
		$query_args = array_merge(['attachment', 'inherit'], $mime_types);
		$countDocs = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->posts}
			WHERE post_type = %s AND post_status = %s
			AND post_mime_type IN ({$placeholders})",
				$query_args
			)
		);
		$total += $countDocs;

		// Posts Categories
		$postTypes = get_post_types(['public' => true]);
		$exclude = ['page', 'attachment', 'revision', 'nav_menu_item', 'product'];
		$postTypes = array_diff($postTypes, $exclude);
		if (!in_array('post', $postTypes, true)) {
			$postTypes[] = 'post';
		}
		$hierarchicalTaxonomies = [];
		foreach ($postTypes as $postType) {
			$taxonomies = get_object_taxonomies($postType, 'objects');
			foreach ($taxonomies as $name => $taxonomy) {
				if ($taxonomy->hierarchical) {
					$hierarchicalTaxonomies[$name] = $name;
				}
			}
		}
		$countPostsCat = 0;
		if (!empty($hierarchicalTaxonomies)) {
			$taxPlaceholders = implode(',', array_fill(0, count($hierarchicalTaxonomies), '%s'));
			$countPostsCat = (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(term_id) FROM {$wpdb->term_taxonomy}
				WHERE taxonomy IN ({$taxPlaceholders})",
					array_values($hierarchicalTaxonomies)
				)
			);
			$total += $countPostsCat;
		}

		// Posts
		$postTypesForPosts = get_post_types(['public' => true, '_builtin' => false]);
		$postTypesForPosts[] = 'post';
		$excludePosts = ['page', 'attachment', 'revision', 'nav_menu_item', 'product'];
		$postTypesForPosts = array_values(array_diff($postTypesForPosts, $excludePosts));
		$countPosts = 0;
		if (!empty($postTypesForPosts)) {
			$postTypePlaceholders = implode(',', array_fill(0, count($postTypesForPosts), '%s'));
			$queryArgs = array_merge(['publish'], $postTypesForPosts);
			$countPosts = (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(*) FROM {$wpdb->posts}
				WHERE post_status = %s AND post_type IN ({$postTypePlaceholders})",
					$queryArgs
				)
			);
			$total += $countPosts;
		}

		// Pages
		$countPages = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->posts}
		WHERE post_type = 'page' AND post_status = 'publish'"
		);
		$total += $countPages;

		// Product Categories — taxonomy: product_cat
		$countProdCat = (int) $wpdb->get_var(
			"SELECT COUNT(term_id) FROM {$wpdb->term_taxonomy}
		WHERE taxonomy = 'product_cat'"
		);
		$total += $countProdCat;

		// Product Attributes
		$countAttr = 0;
		if (function_exists('wc_get_attribute_taxonomies')) {
			$globalAttributes = wc_get_attribute_taxonomies();
			if (!empty($globalAttributes)) {
				$attrTaxonomies = array_map(function ($attr) {
					return wc_attribute_taxonomy_name($attr->attribute_name);
				}, $globalAttributes);
				$attrPlaceholders = implode(',', array_fill(0, count($attrTaxonomies), '%s'));
				$countAttr = (int) $wpdb->get_var(
					$wpdb->prepare(
						"SELECT COUNT(term_id) FROM {$wpdb->term_taxonomy}
					WHERE taxonomy IN ({$attrPlaceholders})",
						$attrTaxonomies
					)
				);
				$total += $countAttr;
			}
		}

		// Products
		$countProducts = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->posts}
		WHERE post_type = 'product' AND post_status = 'publish'"
		);
		$total += $countProducts;

		return $total > 0 ? $total : 1;
	}

	public function runStep(string $step, array $meta = []) {
		$athenaReindexStatus = get_option('athena_reindex_status', []);
		if (empty($athenaReindexStatus['running']))
			return;

		$config = get_option('athena_options', []);
		$token = $config['dashboard_website_token'] ?? '';
		if (empty($token)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Missing Dashboard Website Token', 'error');
			// Debug Log
			throw new Exception('Missing Dashboard Website Token');
		}

		switch ($step) {
			case 'brands_manufacturers':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 1/8: Syncing Brands/Manufacturers', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 1/8: Syncing Brands/Manufacturers');

				$count = $this->syncBrandsManufacturers($token);
				$this->scheduleNext('documents', [], $count);
				break;

			case 'documents':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 2/8: Syncing Documents', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 2/8: Syncing Documents');

				$count = $this->syncDocuments($token);
				$this->scheduleNext('posts_categories', [], $count);
				break;

			case 'posts_categories':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 3/8: Syncing Posts Categories', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 3/8: Syncing Posts Categories');

				$count = $this->syncPostsCategories($token);
				$this->scheduleNext('posts', [], $count);
				break;

			case 'posts':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 4/8: Syncing Posts', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 4/8: Syncing Posts');

				$count = $this->syncPosts($token);
				$this->scheduleNext('pages', [], $count);
				break;

			case 'pages':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 5/8: Syncing Pages', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 5/8: Syncing Pages');

				$count = $this->syncPages($token);
				$this->scheduleNext('product_categories', [], $count);
				break;

			case 'product_categories':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 6/8: Syncing Product Categories', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 6/8: Syncing Product Categories');

				$count = $this->syncProductCategories($token);
				$this->scheduleNext('product_attributes', [], $count);
				break;

			case 'product_attributes':
				// Admin "Show Logs" view
				AthenaLogger::reindexLogs('Reindex Step 7/8: Syncing Product Attributes', 'info');
				// Debug Log
				AthenaLogger::info('Reindex Step 7/8: Syncing Product Attributes');

				$count = $this->syncProductAttributes($token);

				// Calculate total products
				$productsController = new ProductsController();
				$totalProducts = $productsController->getProductsCount();

				$athenaReindexStatus = get_option('athena_reindex_status', []);
				$athenaReindexStatus['total_products'] = $totalProducts;
				update_option('athena_reindex_status', $athenaReindexStatus);

				if ($totalProducts > 0) {
					$this->scheduleNext('products_batch', ['offset' => 0], $count);
				} else {
					// Admin "Show Logs" view
					AthenaLogger::reindexLogs('No products found, skipping.', 'info');
					// Debug Log
					AthenaLogger::info('No products found, skipping.');

					$this->markDone();
				}
				break;

			case 'products_batch':
				$offset = $meta['offset'] ?? 0;
				$batchSize = 500;
				$this->processProductBatch($token, $offset, $batchSize);
				break;
		}
	}

	private function scheduleNext($step, $meta = [], $newly_synced_count = 0) {
		$athenaReindexStatus = get_option('athena_reindex_status', []);

		if ($newly_synced_count > 0) {
			$athenaReindexStatus['total_items_synced'] = ($athenaReindexStatus['total_items_synced'] ?? 0) + $newly_synced_count;
			$totalToSync = $athenaReindexStatus['total_items_to_sync'] ?? 1;
			$percentage = ($athenaReindexStatus['total_items_synced'] / $totalToSync) * 100;
			$athenaReindexStatus['progress_percentage'] = min(max(round($percentage), 0), 100);
		}

		if ($step !== 'done') {
			$args = array_merge($meta, ['step' => $step]);
			wp_schedule_single_event(time() + 2, 'athena_reindex_step_hook', [$args]);

			// Runs wp cron immediately, doesn't wait for visits
			spawn_cron();
		}

		$athenaReindexStatus['step'] = $step;
		if (isset($meta['offset'])) {
			$athenaReindexStatus['offset'] = $meta['offset'];
		}
		update_option('athena_reindex_status', $athenaReindexStatus);
	}

	private function markDone() {
		$athenaReindexStatus = get_option('athena_reindex_status', []);
		$athenaReindexStatus['running'] = false;
		$athenaReindexStatus['progress_percentage'] = 100;
		update_option('athena_reindex_status', $athenaReindexStatus);
		AthenaLogger::reindexLogs('Reindex completed successfully!', 'success');
	}

	private function syncBrandsManufacturers($token) {
		$BrandsManufacturersController = new BrandsManufacturersController();
		$data = $BrandsManufacturersController->getBrandsManufacturers();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No brands/manufacturers found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No brands/manufacturers found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('open-api/reindex/brand-manufacturer', [
			'token' => $token,
			'data' => $data
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Brands/Manufacturers failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Brands/Manufacturers: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Brands/Manufacturers synced successfully.', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Brands/Manufacturers sync success: ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function syncDocuments($token) {
		$DocumentsController = new DocumentsController();
		$data = $DocumentsController->getDocuments();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No documents found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No documents found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('open-api/reindex/documents/async', [
			'token' => $token,
			'data' => $data
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Documents failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Documents: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Documents sync success: ', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Documents sync success: ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function syncPostsCategories($token) {
		$postsCategoriesController = new PostsCategoriesController();
		$data = $postsCategoriesController->getCategories();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No posts categories found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No posts categories found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('open-api/reindex/blog-categories', [
			'token' => $token,
			'data' => $data
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Posts Categories failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Posts Categories: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Posts Categories sync success', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Posts Categories sync success: ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function syncPosts($token) {
		$postsController = new PostsController();
		$data = $postsController->getPosts();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No posts found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No posts found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('open-api/reindex/blog-posts/async', [
			'token' => $token,
			'data' => $data
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Posts failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Posts: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Posts sync success.', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Posts sync success: ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function syncPages($token) {
		$pagesController = new PagesController();
		$data = $pagesController->getPages();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No pages found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No pages found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('open-api/reindex/cms-pages/async', [
			'token' => $token,
			'data' => $data
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Pages failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Pages: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Pages sync success.', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Pages sync success ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function syncProductCategories($token) {
		$productCategoriesController = new ProductCategoriesController();
		$data = $productCategoriesController->getCategories();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No product categories found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No product categories found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('mapping/categories', [
			'token' => $token,
			'data' => ['categories' => $data]
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Product Categories failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Product Categories: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Product Categories sync success', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Product Categories sync success: ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function syncProductAttributes($token) {
		$productAttributesController = new ProductAttributesController();
		$data = $productAttributesController->getAttributes();

		if (empty($data)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('No product attributes found, skipping.', 'info');
			// Debug Log
			AthenaLogger::info('No product attributes found, skipping.');

			return 0;
		}

		$resp = authorized_fetch('mapping/attributes', [
			'token' => $token,
			'data' => ['attributes' => $data]
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Product Attributes failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Product Attributes: ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Product Attributes sync success.', 'success');
			// Debug Log
			AthenaLogger::debug(function() use ($resp) {
				return 'Product Attributes sync success: ' . print_r($resp, true);
			});
		}

		return count($data);
	}

	private function processProductBatch($token, $offset, $batchSize) {
		$athenaReindexStatus = get_option('athena_reindex_status', []);
		$total = $athenaReindexStatus['total_products'] ?? 0;
		// Debug Log
		AthenaLogger::info("Reindex Step 8/8: Processing products batch - offset {$offset} / total {$total}");

		$productsController = new ProductsController();
		$ids = $productsController->getProductIds($batchSize, $offset);

		if (empty($ids)) {
			// Done
			$this->markDone();
			return;
		}

		$products = $productsController->getProductsByIds($ids);
		$athenaReindexStatus = get_option('athena_reindex_status', []);
		$total = $athenaReindexStatus['total_products'] ?? 0;

		$isFirst = ($offset === 0);
		$nextOffset = $offset + $batchSize;
		$isLast = ($nextOffset >= $total);

		$currentBatchNumber = ($offset / $batchSize) + 1;
		$totalBatches = ceil($total / $batchSize);

		// Send batch
		$resp = authorized_fetch('products/rawData', [
			'token' => $token,
			'start_reindex' => $isFirst,
			'end_reindex' => $isLast,
			'data' => ['products' => $products]
		]);

		if (is_wp_error($resp)) {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Product batch ' . $currentBatchNumber . '/' . $totalBatches . ' failed to sync successfully.', 'error');
			// Debug Log
			throw new Exception('API Error - Products batch ' . $currentBatchNumber . '/' . $totalBatches . ': ' . $resp->get_error_message());
		} else {
			// Admin "Show Logs" view
			AthenaLogger::reindexLogs('Reindex Step 8/8: Successfully synced batch ' . $currentBatchNumber . '/' . $totalBatches . ' (' . count($products) . ' products)', 'success');
			// Debug Log
			AthenaLogger::info('Reindex Step 8/8: Successfully synced batch ' . $currentBatchNumber . '/' . $totalBatches . ' (' . count($products) . ' products)');
			AthenaLogger::debug(function() use ($resp) {
				return print_r($resp, true);
			});
		}

		// Schedule next or finish
		if (!$isLast) {
			$this->scheduleNext('products_batch', ['offset' => $nextOffset], count($products));
		} else {
			$this->scheduleNext('done', [], count($products));
			$this->markDone();
		}
	}
}