<?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\RSS;

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

use Wisewand\Logging\Logger;

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Direct database queries required for custom plugin tables with dynamic conditions
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log -- error_log() is only used within WP_DEBUG conditionals for debugging

class RSS_Feed_Manager {

	private $logger;
	private $repository;

	public function __construct( Logger $logger, \Wisewand\Repositories\RSS_Repository $repository = null ) {
		$this->logger = $logger;
		// Backwards compatibility: if repository not passed, create it (or handle legacy)
		// Ideally, we enforce injection, but for safety during refactor:
		$this->repository = $repository ?: new \Wisewand\Repositories\RSS_Repository();
	}

	/**
	 * Sync feed data from Wisewand API to local database
	 * Creates new feed or updates existing
	 *
	 * @param array $wisewand_feed_data Feed data from Wisewand API
	 * @return int|WP_Error Feed ID or error
	 */
	public function sync_feed_from_wisewand( $wisewand_feed_data ) {
		$wisewand_feed_id = $wisewand_feed_data['id'] ?? '';
		if ( empty( $wisewand_feed_id ) ) {
			return new \WP_Error( 'invalid_feed', __( 'Feed ID missing from Wisewand data', 'wisewand-ai-content-writer-seo-content-generator' ) );
		}

		// Check if feed exists
		$existing = $this->get_feed_by_wisewand_id( $wisewand_feed_id );

		// Determine feed_type from entity_type with validation
		$feed_type = 'articles'; // Safe default
		if ( isset( $wisewand_feed_data['entity_type'] ) ) {
			$entity_type = sanitize_text_field( $wisewand_feed_data['entity_type'] );
			// Validate against allowed types
			$valid_types = array( 'articles', 'discoverarticles' );
			if ( in_array( $entity_type, $valid_types, true ) ) {
				$feed_type = $entity_type;
			} else {
				// Log warning for unknown entity_type
				$this->logger->warning(
					'Unknown entity_type encountered, defaulting to articles',
					array(
						'entity_type'      => $entity_type,
						'wisewand_feed_id' => $wisewand_feed_id,
						'feed_name'        => $wisewand_feed_data['title'] ?? 'unknown',
					)
				);
			}
		} else {
			// Log info when entity_type is missing
			$this->logger->info(
				'Missing entity_type in feed data, defaulting to articles',
				array(
					'wisewand_feed_id' => $wisewand_feed_id,
					'feed_name'        => $wisewand_feed_data['title'] ?? 'unknown',
				)
			);
		}

		$feed_data = array(
			'wisewand_feed_id'    => sanitize_text_field( $wisewand_feed_id ),
			'feed_type'           => $feed_type,
			'feed_name'           => sanitize_text_field( $wisewand_feed_data['title'] ?? '' ),
			'feed_url'            => esc_url_raw( $wisewand_feed_data['url'] ?? '' ),
			'source_url'          => esc_url_raw( $wisewand_feed_data['url'] ?? '' ),
			'feed_icon'           => ! empty( $wisewand_feed_data['icon'] ) ? esc_url_raw( $wisewand_feed_data['icon'] ) : null,
			'feed_color'          => ! empty( $wisewand_feed_data['color'] ) ? sanitize_hex_color( $wisewand_feed_data['color'] ) : null,
			'wisewand_status'     => $wisewand_feed_data['is_active'] ? 'active' : 'inactive',
			'wisewand_frequency'  => sanitize_text_field( $wisewand_feed_data['frequency'] ?? '24h' ),
			'wisewand_limit'      => absint( $wisewand_feed_data['limit'] ?? 1 ),
			'wisewand_cost'       => floatval( $wisewand_feed_data['cost'] ?? 2 ),
			'wisewand_project_id' => ! empty( $wisewand_feed_data['config']['project_id'] ) ? sanitize_text_field( $wisewand_feed_data['config']['project_id'] ) : null,
			'last_fetched_at'     => ! empty( $wisewand_feed_data['last_fetched_at'] ) ? gmdate( 'Y-m-d H:i:s', strtotime( $wisewand_feed_data['last_fetched_at'] ) ) : null,
			'updated_at'          => current_time( 'mysql' ),
		);

		if ( $existing ) {
			// Update existing feed
			$result = $this->repository->save_feed( array_merge( array( 'id' => $existing['id'] ), $feed_data ) );

			if ( $result === false ) {
				return new \WP_Error( 'update_failed', __( 'Failed to update feed', 'wisewand-ai-content-writer-seo-content-generator' ) );
			}

			$this->logger->info(
				'Feed synced from Wisewand (updated)',
				array(
					'feed_id'          => $existing['id'],
					'wisewand_feed_id' => $wisewand_feed_id,
					'feed_name'        => $feed_data['feed_name'],
				)
			);

			return $existing['id'];
		} else {
			// Create new feed
			$feed_data['created_at'] = current_time( 'mysql' );
			$feed_data['created_by'] = get_current_user_id();

			$feed_id = $this->repository->save_feed( $feed_data );

			if ( ! $feed_id ) {
				return new \WP_Error( 'insert_failed', __( 'Failed to create feed', 'wisewand-ai-content-writer-seo-content-generator' ) );
			}

			$this->logger->info(
				'Feed synced from Wisewand (created)',
				array(
					'feed_id'          => $feed_id,
					'wisewand_feed_id' => $wisewand_feed_id,
					'feed_name'        => $feed_data['feed_name'],
				)
			);

			return $feed_id;
		}
	}

	/**
	 * Get feed by Wisewand ID
	 *
	 * @param string $wisewand_feed_id Wisewand feed ID
	 * @return array|null Feed data or null
	 */
	public function get_feed_by_wisewand_id( $wisewand_feed_id ) {
		$feed = $this->repository->get_feed_by_uuid( $wisewand_feed_id );
		return $feed ? (array) $feed : null;
	}

	/**
	 * Get feed by local ID
	 *
	 * @param int $feed_id Local feed ID
	 * @return array|null Feed data or null
	 */
	public function get_feed( $feed_id ) {
		$feed = $this->repository->get_feed( $feed_id );
		$feed = $feed ? (array) $feed : null;

		// v1.3.0: Type cast integer fields (MySQL returns as strings with ARRAY_A)
		// This fixes JavaScript conditionals treating string "0" as truthy
		if ( $feed ) {
			$feed['id']                              = (int) $feed['id'];
			$feed['auto_import_enabled']             = (int) $feed['auto_import_enabled'];
			$feed['use_ai_category_matching']        = (int) $feed['use_ai_category_matching'];
			$feed['use_custom_publication_schedule'] = (int) $feed['use_custom_publication_schedule'];
			$feed['max_articles_per_sync']           = (int) $feed['max_articles_per_sync'];
			$feed['custom_interval_hours']           = (int) $feed['custom_interval_hours'];
			$feed['target_author']                   = (int) $feed['target_author'];
			$feed['total_articles']                  = (int) $feed['total_articles'];
			$feed['articles_imported']               = (int) $feed['articles_imported'];
			$feed['articles_pending']                = (int) $feed['articles_pending'];
			$feed['articles_failed']                 = (int) $feed['articles_failed'];
		}

		return $feed ?: null;
	}

	/**
	 * Get all feeds with optional filters
	 *
	 * Performance optimization: Added LIMIT clause to prevent memory exhaustion
	 * with large datasets. Default limit is 1000 feeds (more than enough for
	 * typical WordPress installations).
	 *
	 * @param array $filters Optional filters (supports 'limit' parameter).
	 * @return array Array of feeds
	 */
	public function get_all_feeds( $filters = array() ) {
		$feeds = $this->repository->get_all_feeds( $filters );

		// v1.3.0: Type cast integer fields for all feeds (same as get_feed)
		if ( $feeds ) {
			foreach ( $feeds as &$feed ) {
				$feed['id']                              = (int) $feed['id'];
				$feed['auto_import_enabled']             = (int) $feed['auto_import_enabled'];
				$feed['use_ai_category_matching']        = (int) $feed['use_ai_category_matching'];
				$feed['use_custom_publication_schedule'] = (int) $feed['use_custom_publication_schedule'];
				$feed['max_articles_per_sync']           = (int) $feed['max_articles_per_sync'];
				$feed['custom_interval_hours']           = (int) $feed['custom_interval_hours'];
				$feed['target_author']                   = (int) $feed['target_author'];
				$feed['total_articles']                  = (int) $feed['total_articles'];
				$feed['articles_imported']               = (int) $feed['articles_imported'];
				$feed['articles_pending']                = (int) $feed['articles_pending'];
				$feed['articles_failed']                 = (int) $feed['articles_failed'];
			}
		}

		return $feeds;
	}

	/**
	 * Update feed settings (automation and import configuration)
	 *
	 * @param int   $feed_id Feed ID
	 * @param array $settings Settings to update
	 * @return bool|WP_Error Success or error
	 */
	public function update_feed_settings( $feed_id, $settings ) {
		global $wpdb;

		// VERSION MARKER: v2.1-fixed (2025-01-03) - This ensures new code is loaded
		$version = 'v2.1-fixed';

		$allowed_fields = array(
			'enabled_for_site', // RSS v2.0: Feed activation status (legacy)
			'sync_status',      // RSS v2.0: Actual status field in database
			'auto_import_enabled',
			'import_frequency',
			'import_schedule_type',
			'import_schedule_time',
			'import_schedule_days',
			'max_articles_per_sync',
			'import_status_filter',
			'target_post_status',
			'publish_mode',                    // RSS v2.1: Publication mode (schedule, publish, draft, global)
			'target_author',
			'target_categories',
			'target_tags',
			'target_language',  // WPML: Target language for imported articles
			'use_ai_category_matching',
			'custom_interval_hours',           // RSS v2.0: Custom sync interval
			'use_custom_publication_schedule', // RSS v2.0: Publication control
			'publication_time_range',          // RSS v2.0: Time range preference
			'publication_allowed_days',        // RSS v2.0: Allowed days JSON
		);

		$update_data = array();
		$format      = array();

		foreach ( $settings as $key => $value ) {
			// Check if field is allowed
			$is_allowed = in_array( $key, $allowed_fields );

			if ( $is_allowed ) {
				$update_data[ $key ] = $value;

				// Determine format
				if ( in_array( $key, array( 'enabled_for_site', 'auto_import_enabled', 'use_ai_category_matching', 'target_author', 'max_articles_per_sync', 'custom_interval_hours', 'use_custom_publication_schedule' ) ) ) {
					$format[] = '%d';
				} elseif ( $key === 'sync_status' ) {
					$format[] = '%s';  // sync_status is a string field ('enabled'/'disabled')
				} else {
					$format[] = '%s';
				}
			}
		}

		if ( empty( $update_data ) ) {
			return new \WP_Error( 'no_data', __( 'No valid settings to update', 'wisewand-ai-content-writer-seo-content-generator' ) );
		}

		$update_data['updated_at'] = current_time( 'mysql' );
		$format[]                  = '%s';

		$result = $this->repository->update_feed_settings( $feed_id, $update_data );

		if ( $result === false ) {
			return new \WP_Error( 'update_failed', __( 'Failed to update feed settings', 'wisewand-ai-content-writer-seo-content-generator' ) );
		}

		$this->logger->info(
			'Feed settings updated',
			array(
				'feed_id'  => $feed_id,
				'settings' => array_keys( $update_data ),
			)
		);

		return true;
	}

	/**
	 * Update feed statistics
	 *
	 * @param int   $feed_id Feed ID
	 * @param array $stats Statistics to update
	 * @return bool Success
	 */
	public function update_feed_statistics( $feed_id, $stats ) {
		global $wpdb;

		$update_data = array();

		if ( isset( $stats['total_articles'] ) ) {
			$update_data['total_articles'] = absint( $stats['total_articles'] );
		}
		if ( isset( $stats['articles_success'] ) ) {
			$update_data['articles_success'] = absint( $stats['articles_success'] );
		}
		if ( isset( $stats['articles_error'] ) ) {
			$update_data['articles_error'] = absint( $stats['articles_error'] );
		}
		if ( isset( $stats['articles_imported'] ) ) {
			$update_data['articles_imported'] = absint( $stats['articles_imported'] );
		}
		if ( isset( $stats['articles_pending'] ) ) {
			$update_data['articles_pending'] = absint( $stats['articles_pending'] );
		}
		if ( isset( $stats['articles_failed'] ) ) {
			$update_data['articles_failed'] = absint( $stats['articles_failed'] );
		}
		if ( isset( $stats['last_sync_at'] ) ) {
			$update_data['last_sync_at'] = $stats['last_sync_at'];
		}
		if ( isset( $stats['last_import_at'] ) ) {
			$update_data['last_import_at'] = $stats['last_import_at'];
		}

		if ( empty( $update_data ) ) {
			return true;
		}

		$update_data['updated_at'] = current_time( 'mysql' );

		$this->repository->update_feed_statistics( $feed_id, $update_data );

		return true;
	}

	/**
	 * Get feeds due for synchronization
	 *
	 * Performance optimization: Added LIMIT clause to prevent processing
	 * too many feeds at once. Default limit is 100 feeds per sync cycle.
	 *
	 * @param int $limit Maximum number of feeds to return (default 100).
	 * @return array Feeds that need syncing
	 */
	public function get_feeds_due_for_sync( $limit = 100 ) {
		return $this->repository->get_feeds_due_for_sync( $limit );
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		return $feeds;
	}

	/**
	 * Calculate and set next sync time based on schedule settings
	 *
	 * @param int $feed_id Feed ID
	 * @return bool Success
	 */
	public function calculate_next_sync_time( $feed_id ) {
		global $wpdb;

		$feed = $this->get_feed( $feed_id );
		if ( ! $feed ) {
			return false;
		}

		$schedule_type = $feed['import_schedule_type'] ?? 'every_6_hours';
		$schedule_time = $feed['import_schedule_time'];
		$schedule_days = $feed['import_schedule_days'];

		$next_sync = null;
		$now       = current_time( 'timestamp' );

		switch ( $schedule_type ) {
			case 'hourly':
				// Next hour
				$next_sync = strtotime( '+1 hour', $now );
				break;

			case 'every_6_hours':
				// Next 6-hour interval
				$next_sync = strtotime( '+6 hours', $now );
				break;

			case 'daily':
				// Calculate next daily sync at specific time
				if ( $schedule_time ) {
					$next_sync = $this->calculate_daily_sync_time( $schedule_time, $schedule_days, $now );
				} else {
					$next_sync = strtotime( '+1 day', $now );
				}
				break;

			case 'custom':
				// Use import_frequency field (e.g., "every_12_hours")
				$frequency = $feed['import_frequency'] ?? 'every_6_hours';
				if ( preg_match( '/every_(\d+)_hours/', $frequency, $matches ) ) {
					$hours     = absint( $matches[1] );
					$next_sync = strtotime( "+{$hours} hours", $now );
				} else {
					$next_sync = strtotime( '+6 hours', $now ); // Fallback
				}
				break;

			default:
				$next_sync = strtotime( '+6 hours', $now );
		}

		$this->repository->update_next_sync_time( $feed_id, gmdate( 'Y-m-d H:i:s', $next_sync ) );

		return true;
	}

	/**
	 * Calculate next daily sync time respecting time and days settings
	 *
	 * @param string $schedule_time Time in H:i:s format
	 * @param string $schedule_days Comma-separated day numbers (1-7)
	 * @param int    $from_timestamp Starting timestamp
	 * @return int Next sync timestamp
	 */
	private function calculate_daily_sync_time( $schedule_time, $schedule_days, $from_timestamp ) {
		$target_time = strtotime( $schedule_time );
		$hour        = (int) gmdate( 'H', $target_time );
		$minute      = (int) gmdate( 'i', $target_time );

		// Parse allowed days (1=Monday, 7=Sunday)
		$allowed_days = ! empty( $schedule_days ) ? array_map( 'intval', explode( ',', $schedule_days ) ) : array( 1, 2, 3, 4, 5, 6, 7 );

		// Start with tomorrow at target time
		$candidate = strtotime( 'tomorrow ' . sprintf( '%02d:%02d:00', $hour, $minute ), $from_timestamp );

		// Find next allowed day (max 14 days search)
		for ( $i = 0; $i < 14; $i++ ) {
			$day_of_week = (int) gmdate( 'N', $candidate ); // 1=Monday, 7=Sunday

			if ( in_array( $day_of_week, $allowed_days, true ) ) {
				return $candidate;
			}

			// Try next day
			$candidate = strtotime( '+1 day', $candidate );
		}

		// Fallback: tomorrow at target time
		return strtotime( 'tomorrow ' . sprintf( '%02d:%02d:00', $hour, $minute ), $from_timestamp );
	}

	/**
	 * Delete feed and all associated articles
	 *
	 * @param int $feed_id Feed ID
	 * @return bool Success
	 */
	public function delete_feed( $feed_id ) {
		// Foreign key constraint will auto-delete associated articles
		$result = $this->repository->delete_feed( $feed_id );

		if ( $result ) {
			$this->logger->info( 'Feed deleted', array( 'feed_id' => $feed_id ) );
		}

		return (bool) $result;
	}

	/**
	 * Get total feeds count with optional filters
	 *
	 * @param array $filters Optional filters
	 * @return int Count
	 */
	public function get_feeds_count( $filters = array() ) {
		return $this->repository->get_feeds_count( $filters );
	}

	/**
	 * Get global default publish mode
	 *
	 * Returns the site-wide default for how RSS articles should be published.
	 * Used when feed's publish_mode is set to 'global'.
	 *
	 * @since 1.4.0
	 * @return string Default publish mode (schedule, publish, or draft).
	 */
	public function get_global_publish_mode() {
		return get_option( 'wisewand_rss_default_publish_mode', 'schedule' );
	}

	/**
	 * Set global default publish mode
	 *
	 * Updates the site-wide default for RSS article publication behavior.
	 * Validates input against allowed modes.
	 *
	 * @since 1.4.0
	 * @param string $mode Publish mode (schedule, publish, or draft).
	 * @return bool True if mode was set, false if invalid mode provided.
	 */
	public function set_global_publish_mode( $mode ) {
		$allowed_modes = array( 'schedule', 'publish', 'draft' );

		if ( ! in_array( $mode, $allowed_modes, true ) ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log(
					sprintf(
						'Wisewand RSS: Invalid global publish mode "%s". Allowed: %s',
						$mode,
						implode( ', ', $allowed_modes )
					)
				);
			}
			return false;
		}

		$result = update_option( 'wisewand_rss_default_publish_mode', $mode );

		if ( $result && WP_DEBUG ) {
			error_log(
				sprintf(
					'Wisewand RSS: Global publish mode set to "%s"',
					$mode
				)
			);
		}

		return $result;
	}
}
