<?php
/**
 * External Links — makes links that point off-site open in a new tab.
 *
 * Client-side by design: a tiny front-end script sets target="_blank" +
 * rel="noopener" on every external <a> after the page loads. That catches links
 * from every source (theme, page builder, blocks, widgets, custom HTML) — a
 * server-side the_content filter would miss everything a builder renders outside
 * the_content — and it adds ZERO per-request PHP work, so it is cache-friendly
 * and never touches the request hot path.
 *
 * Opt-in per site: this module ships default-OFF (see the
 * bw_dev_module_default_enabled filter in BW_Dev_Plugin::boot()). Enable it
 * under Settings → BW Dev → Modules on the sites that want it.
 *
 * @package BW_Dev
 */

defined( 'ABSPATH' ) || exit;

class BW_Dev_Module_External_Links implements BW_Dev_Module_Interface {

	public function slug(): string {
		return 'external_links';
	}

	public function label(): string {
		return __( 'External Links', 'bw-dev' );
	}

	public function group(): string {
		return 'frontend';
	}

	public function default_settings(): array {
		return array(
			'nofollow'         => false,
			'internal_domains' => '',
		);
	}

	public function sanitize( array $data ): array {
		return array(
			'nofollow'         => ! empty( $data['nofollow'] ),
			'internal_domains' => isset( $data['internal_domains'] ) ? sanitize_textarea_field( wp_unslash( (string) $data['internal_domains'] ) ) : '',
		);
	}

	public function register(): void {
		// Front-end only (wp_enqueue_scripts never fires in admin/feeds).
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
	}

	public function uninstall(): void {
		// No-op: state lives under bw_dev_settings, dropped by the core uninstall.
	}

	public function enqueue_assets(): void {
		$settings = wp_parse_args( (array) bw_dev()->settings()->get( $this->slug(), null, array() ), $this->default_settings() );

		wp_enqueue_script(
			'bw-dev-external-links',
			BW_DEV_URL . 'assets/js/external-links.js',
			array(),
			BW_DEV_VERSION,
			true
		);
		wp_localize_script(
			'bw-dev-external-links',
			'bwDevExternalLinks',
			array(
				'internalHosts' => $this->internal_hosts( (string) $settings['internal_domains'] ),
				'nofollow'      => ! empty( $settings['nofollow'] ),
			)
		);
	}

	/**
	 * Parse the "additional internal domains" textarea into a list of bare
	 * lowercase hostnames (scheme, path, port and a leading www. stripped). These
	 * are treated as same-site so their links stay in the current tab.
	 *
	 * @return array<int,string>
	 */
	private function internal_hosts( string $raw ): array {
		$hosts = array();
		foreach ( preg_split( '/[\s,]+/', $raw ) as $entry ) {
			$entry = strtolower( trim( (string) $entry ) );
			if ( '' === $entry ) {
				continue;
			}
			$entry = preg_replace( '~^[a-z]+://~', '', $entry ); // strip scheme
			$entry = explode( '/', $entry )[0];                  // strip path
			$entry = explode( ':', $entry )[0];                  // strip port
			$entry = preg_replace( '~^www\.~', '', (string) $entry );
			if ( '' !== $entry ) {
				$hosts[] = $entry;
			}
		}
		return array_values( array_unique( $hosts ) );
	}

	public function render_tab(): void {
		$prefix   = BW_Dev_Settings::OPTION . '[' . $this->slug() . ']';
		$settings = wp_parse_args( (array) bw_dev()->settings()->get( $this->slug(), null, array() ), $this->default_settings() );
		?>
		<p class="description">
			<?php esc_html_e( 'Makes links that point to another website open in a new browser tab. Links to this site (and to any domains you list below) keep opening in the same tab. Anchor links, mailto: and tel: links are left alone.', 'bw-dev' ); ?>
		</p>
		<p class="description">
			<?php esc_html_e( 'This runs in the visitor\'s browser, so it works with every theme and page builder and does not slow the site down. For security, external links also get rel="noopener".', 'bw-dev' ); ?>
		</p>

		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row"><label for="bw-dev-extlinks-internal"><?php esc_html_e( 'Also treat these domains as internal', 'bw-dev' ); ?></label></th>
					<td>
						<textarea id="bw-dev-extlinks-internal" name="<?php echo esc_attr( $prefix . '[internal_domains]' ); ?>" rows="4" class="large-text code" placeholder="shop.example.com&#10;example.net"><?php echo esc_textarea( (string) $settings['internal_domains'] ); ?></textarea>
						<p class="description"><?php esc_html_e( 'Optional. One domain per line (or comma-separated). Links to these open in the same tab — useful for a separate shop/subdomain you consider part of this site.', 'bw-dev' ); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Search-engine hint', 'bw-dev' ); ?></th>
					<td>
						<label>
							<input type="checkbox" name="<?php echo esc_attr( $prefix . '[nofollow]' ); ?>" value="1" <?php checked( ! empty( $settings['nofollow'] ) ); ?> />
							<?php esc_html_e( 'Also add rel="nofollow" to external links', 'bw-dev' ); ?>
						</label>
						<p class="description"><?php esc_html_e( 'Off by default. Tells search engines not to pass ranking to the sites you link out to. Leave off unless you specifically want that.', 'bw-dev' ); ?></p>
					</td>
				</tr>
			</tbody>
		</table>
		<?php
	}
}
