<?php
namespace ActirisePublic\Includes;

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

use Actirise\Includes\Api;
use Actirise\Includes\Cron;
use Actirise\Includes\Helpers;
use Actirise\Includes\Logger;
use Actirise\Includes\Options;

/**
 * Actirise Ads.txt management
 *
 * @link       https://actirise.com
 * @since      2.0.0
 * @package    actirise
 * @subpackage actirise/public/includes
 * @author     actirise <wordpress@actirise.com>
 */
final class AdsTxt {
	/**
	 * Ads.txt constructor.
	 *
	 * @since    2.0.0
	 */
	public function __construct() {
		if ( ACTIRISE_CRON !== 'true' ) {
			$this->check_adstxt_update();
		}
	}

	/**
	 * Check if hosted ads.txt is enabled
	 *
	 * @since 3.3.1
	 * @return bool
	 */
	private static function hosted_adstxt_enabled() {
		$hosted_enabled = Options::get( 'hosted-adstxt-enabled', false );

		return $hosted_enabled === true;
	}

	/**
	 * The loader of this plugin.
	 *
	 * @since    2.0.0
	 * @return void
	 */
	public function init() {
		// Check if hosted ads.txt is enabled
		if ( self::hosted_adstxt_enabled() ) {
			$request = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : false; // @phpstan-ignore-line

			if ( $request === '/ads.txt' ) {
				$this->redirect_to_hosted_adstxt();
			}
		}

		// Standard ads.txt handling
		if ( Options::get( 'adstxt-active', false ) !== false ) {
			$request = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : false; // @phpstan-ignore-line

			if ( $request === '/ads.txt' && Options::get( 'adstxt-actirise', false ) !== false ) {
				$this->render_adstxt();
			}
		}
	}

	/**
	 * Get hosted ads.txt URL
	 *
	 * @since 3.3.1
	 * @return string|false The hosted ads.txt URL or false if not available
	 */
	private static function get_hosted_url() {
		$uuid = Options::get( 'website-uuid', '' );

		if ( empty( $uuid ) ) {
			Logger::add_log( 'get_hosted_url: no website uuid', 'public/include/adstxt', 'error' );
			return false;
		}

		$url = 'https://gateway.actirise.com/services/adstxt/' . $uuid;

		return $url;
	}

	/**
	 * Redirect to hosted ads.txt
	 *
	 * @since 3.3.1
	 * @return void
	 */
	private function redirect_to_hosted_adstxt() {
		$url = self::get_hosted_url();

		if ( $url === false ) {
			Logger::add_log( 'redirect_to_hosted_adstxt: URL not available', 'public/include/adstxt', 'error' );
			// Fallback to standard ads.txt rendering
			if ( Options::get( 'adstxt-actirise', false ) !== false ) {
				$this->render_adstxt();
			}
			return;
		}

		$allowed_host = wp_parse_url( $url, PHP_URL_HOST );

		add_filter(
			'allowed_redirect_hosts',
			static function ( $hosts ) use ( $allowed_host ) {
				if ( is_string( $allowed_host ) && $allowed_host !== '' ) {
					$hosts[] = $allowed_host;
				}

				return $hosts;
			}
		);

		wp_safe_redirect( $url, 302 );
		exit;
	}

	/**
	 * Generate abd render ads.txt
	 *
	 * @since    2.0.0
	 * @return void
	 */
	private function render_adstxt() {
		header( 'Content-Type: text/plain' );
		header( 'Actirise: true' );
		header( 'Cache-Control: no-cache, must-revalidate' );
		header( 'Expires: Thu, 29 Dec 1994 12:25:00 GMT' );
		header( 'Pragma: no-cache' );

		$adstxt = self::get_adstxt( true );

		header( 'ETag: W/"' . md5( $adstxt ) . '"' );

		/** @var string $adstxt_filter */
		$adstxt_filter = apply_filters( 'actirise_ads_txt_content', $adstxt );

		echo esc_html( $adstxt_filter );
		die();
	}

	/**
	 * Clean duplicate line
	 *
	 * @since    2.4.0
	 * @param string $adstxt
	 * @param string $lines
	 *
	 * @return string
	 */
	private static function clean_duplicate_line( $adstxt, $lines ) {
		$lines = explode( "\n", $lines );
		$lines = array_unique( $lines );

		foreach ( $lines as $key => &$line ) {
			$exploded_line = explode( ',', $line );

			if ( count( $exploded_line ) > 0 ) {
				$exploded_line[0] = strtolower( $exploded_line[0] );
			}

			if ( count( $exploded_line ) >= 2 ) {
				$exploded_line[2] = strtoupper( $exploded_line[2] );
			}

			$line = implode( ',', $exploded_line );

			if ( $line !== '' ) {
				if ( false !== strpos( $adstxt, $line ) ) {
					unset( $lines[ $key ] );
				}
			}
		}

		$lines = implode( "\n", $lines );

		return $lines;
	}

		/**
		 * Get ads.txt formated
		 *
		 * @since    2.0.0
		 * @param bool $custom
		 * @return string
		 */
	public static function get_adstxt( $custom = false ) {
		/** @var string $adstxt_actirise */
		$adstxt_actirise = Options::get( 'adstxt-actirise', '' );

		preg_match( '/OWNERDOMAIN=(.*)\n/', $adstxt_actirise, $matches );
		$owner_domain = '';

		if ( isset( $matches[1] ) ) {
			$owner_domain = $matches[1];
		}

		preg_match( '/MANAGERDOMAIN=(.*)\n/', $adstxt_actirise, $matches );
		$manager_domain = '';

		if ( isset( $matches[1] ) ) {
			$manager_domain = $matches[1];
		}

		/** @var string $adstxt_actirise */
		$adstxt_actirise = preg_replace( '/MANAGERDOMAIN=.*\n/', '', $adstxt_actirise );
		/** @var string $adstxt_actirise */
		$adstxt_actirise = preg_replace( '/OWNERDOMAIN=.*\n/', '', $adstxt_actirise );
		/** @var string $adstxt_actirise */
		$adstxt_actirise = preg_replace( '/## START actirise.com ##\n/', '', $adstxt_actirise );
		/** @var string $adstxt_actirise */
		$adstxt_actirise = preg_replace( '/## END actirise.com ##/', '', $adstxt_actirise );

		/** @var string $adstxt */
		$adstxt  = '#---------------------------- Monetized by Actirise WordPress Plugin ----------------------------';
		$adstxt .= "\n";
		$adstxt .= "\n";
		$adstxt .= 'contact=hello@actirise.com';
		$adstxt .= "\n";
		$adstxt .= "\n";

		if ( $owner_domain !== '' ) {
			$adstxt .= 'OWNERDOMAIN=' . $owner_domain;
			$adstxt .= "\n";
		}

		if ( $manager_domain !== '' ) {
			$adstxt .= 'MANAGERDOMAIN=' . $manager_domain;
			$adstxt .= "\n";
		}

		/** @var string $adstxt */
		$adstxt .= $adstxt_actirise;
		$adstxt .= '#--------------------------------------- End Actirise.com ---------------------------------------';

		if ( $custom ) {
			/** @var string $adstxt_custom */
			$adstxt_custom = Options::get( 'adstxt-custom' );
			/** @var array<\stdClass> $adstxt_custom_array */
			$adstxt_custom_array = array();

			if ( $adstxt_custom ) {
				/** @var array<\stdClass> $adstxt_custom_array */
				$adstxt_custom_array = json_decode( $adstxt_custom );
			}

			if ( count( $adstxt_custom_array ) > 0 ) {
				$adstxt .= "\n";

				foreach ( $adstxt_custom_array as $adstxt_item ) {
					$cleaned_line = self::clean_duplicate_line( $adstxt, $adstxt_item->value ); // @phpstan-ignore argument.type

					if ( $cleaned_line !== '' ) {
						$adstxt .= "\n";
						$adstxt .= '## START ' . $adstxt_item->title . " ##\n";
						$adstxt .= $cleaned_line . "\n";
						$adstxt .= '## END ' . $adstxt_item->title . " ##\n";
					}
				}
			}
		}

		return $adstxt;
	}

	/**
	 * Get ads.txt from API
	 *
	 * @since    2.0.0
	 *
	 * @return bool|string
	 */
	public static function get_from_api() {
		$args = array(
			'domain' => rawurlencode( Helpers::get_server_details()['host'] ),
		);

		if ( Options::get( 'settings-uuid-type', 'boot' ) === 'universal' ) {
			$args['universal'] = 'true';
			$args['product']   = '3';
		}

		/** @var string $uuid */
		$uuid     = Options::get( 'settings-uuid' );
		$api_url  = 'adstxt_files/' . $uuid;
		$api      = new Api();
		$response = $api->get( 'api', $api_url, $args );

		if ( is_wp_error( $response ) ) {
			Logger::add_log( 'get_from_api error ' . $response->get_error_code(), 'public/include/adstxt', 'error' );
			return false;
		}

		if ( ! is_array( $response ) ) {
			Logger::add_log( 'get_from_api response is not array ', 'public/include/adstxt', 'error' );
			return false;
		}

		if ( ! isset( $response['datas'] ) ) {
			Logger::add_log( 'get_from_api response is empty ', 'public/include/adstxt', 'error' );
			return false;
		}

		$array_response = array( $response['datas'] );

		if ( count( $array_response ) !== 1 ) {
			Logger::add_log( 'get_from_api responselength is up to 1 ', 'public/include/adstxt', 'error' );
			return false;
		}

		if ( empty( $array_response[0] ) ) {
			Logger::add_log( 'get_from_api responselength[0] is not exist ', 'public/include/adstxt', 'error' );
			return false;
		}

		/** @var array{'datas': string} $response */
		return $response['datas'];
	}

	/**
	 * Check ads.txt file using API
	 *
	 * @since 3.0.0
	 * @param string $force
	 * @return bool|array<mixed, mixed>
	 */
	public static function check_from_api( $force ) {
		/** @var string $token */
		$token   = Options::get( 'settings-analytics-token', '' );
		$refresh = $force === 'true' ? 1 : 0;

		if ( $token !== '' ) {
			$website_id = Options::get( 'website-id' );
			$api        = new Api();
			$response   = $api->get(
				'api',
				'websites/' . $website_id . '/adstxt_monitorings',
				array( 'refresh' => $refresh ),
				$token
			);

			if ( is_wp_error( $response ) ) {
				Logger::add_log( 'check_from_api error ' . $response->get_error_code(), 'public/include/adstxt', 'error' );
				return false;
			}

			if ( ! is_array( $response ) ) {
				Logger::add_log( 'check_from_api response is not array ', 'public/include/adstxt', 'error' );
				return false;
			}

			// Store missing and blacklist counts for health check
			self::store_validation_counts( $response );

			return $response;
		}

		return false;
	}

	/**
	 * Store ads.txt validation counts for health check
	 *
	 * @since 3.3.0
	 * @param array<mixed> $response API response.
	 * @return void
	 */
	private static function store_validation_counts( $response ) {
		$missing_count   = 0;
		$blacklist_count = 0;

		if ( isset( $response['missing'] ) ) {
			if ( is_string( $response['missing'] ) ) {
				// Count lines in string (split by newline)
				$missing_lines = array_filter( explode( "\n", trim( $response['missing'] ) ) );
				$missing_count = count( $missing_lines );
			} elseif ( is_array( $response['missing'] ) ) {
				$missing_count = count( $response['missing'] );
			}
		}

		if ( isset( $response['json']['blacklistLines'] ) && is_array( $response['json']['blacklistLines'] ) ) {
			$blacklist_count = count( $response['json']['blacklistLines'] );
		}

		Options::update( 'adstxt-missing-count', $missing_count );
		Options::update( 'adstxt-blacklist-count', $blacklist_count );
	}

	/**
	 * Update ads.txt file
	 *
	 * @since    2.4.0
	 * @return void
	 */
	public static function update_file() {
		// Don't update file if hosted ads.txt is enabled
		if ( self::hosted_adstxt_enabled() ) {
			Logger::add_log( 'update_file: hosted ads.txt is enabled, skipping file update', 'public/include/adstxt', 'debug' );
			return;
		}

		Logger::add_log( 'update_file start', 'public/include/adstxt', 'debug' );

		try {
			$wp_fs = Helpers::get_wp_fs();

			if ( is_wp_error( $wp_fs ) ) {
				Logger::add_log( 'update_file wp_fs error', 'public/include/adstxt', 'error' );

				Options::update( 'adstxt-file', false );

				return;
			}

			$path_ads_txt = ABSPATH . 'ads.txt';

			$file_exists = $wp_fs->exists( $path_ads_txt );

			if ( is_multisite() ) {
				Options::update( 'adstxt-file', false );
				if ( $file_exists ) {
					Options::update( 'adstxt-existing', true );
				} else {
					Options::update( 'adstxt-existing', false );
				}
				return;
			}

			if ( $file_exists && ! $wp_fs->is_writable( $path_ads_txt ) ) {
				Logger::add_log( 'update_file not writable', 'public/include/adstxt', 'error' );
				Options::update( 'adstxt-file', false );
				Options::update( 'adstxt-existing', true );

				return;
			}

			if ( defined( 'FS_CHMOD_FILE' ) ) {
				$modes = FS_CHMOD_FILE;
			} else {
				$modes = false;
			}

			$success = $wp_fs->put_contents( $path_ads_txt, self::get_adstxt( true ), $modes );

			if ( $success === false ) {
				Logger::add_log( 'update_file put_contents failed', 'public/include/adstxt', 'error' );
				Options::update( 'adstxt-file', false );
				Options::update( 'adstxt-existing', $file_exists );

				return;
			}

			Logger::add_log( 'update_file success', 'public/include/adstxt', 'debug' );

			Options::update( 'adstxt-existing', false );
			Options::update( 'adstxt-file', $success );
			Options::update( 'adstxt-update', false );
		} catch ( \Exception $e ) {
			Logger::add_log( 'update_file catch: ', 'public/include/adstxt', 'error' );

			Options::update( 'adstxt-file', false );
		}
	}

	/**
	 * Delete ads.txt file
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public static function delete_file() {
		Logger::add_log( 'delete_file start', 'public/include/adstxt', 'debug' );

		if ( Options::get( 'adstxt-file' ) !== false ) {
			$ads_txt_file = ABSPATH . 'ads.txt';

			if ( file_exists( $ads_txt_file ) ) {
				wp_delete_file( $ads_txt_file );
			}
		}
	}

	/**
	 * Check if ads.txt need to be updated
	 *
	 * @since    2.0.0
	 * @return void
	 */
	private function check_adstxt_update() {
		$cron = new Cron();
		$cron->check_scheduled_task_with_transient(
			'update_adstxt',
			array(
				$cron,
				'update_adstxt',
			)
		);
	}
}
