<?php
/**
 * @license GPL-2.0-or-later
 * @link    https://wordpress.org/plugins/pressio-crm
 */
namespace PressioCRM\Modules;

defined( 'ABSPATH' ) || exit;

use PressioCRM\Framework\Module;
use WP_Screen;

class AdminModule extends Module {

	public function register(): void {
		// No container bindings — admin UI is purely hook-driven.
	}

	public function boot(): void {
		if ( ! is_admin() ) {
			return;
		}

		add_action( 'admin_menu', [ $this, 'register_menu' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
		add_action( 'admin_head', [ $this, 'enqueue_menu_icon_css' ] );
		// current_screen fires after the screen object is set — get_current_screen()
		// is guaranteed non-null here, unlike admin_init where it can still be null.
		add_action( 'current_screen', [ $this, 'maybe_redirect_onboarding' ] );
		add_action( 'admin_notices', [ $this, 'suppress_notices' ], 1 );
		add_action( 'all_admin_notices', [ $this, 'suppress_notices' ], 1 );
	}

	public function register_menu(): void {
		add_menu_page(
			__( 'Pressio CRM', 'pressio-crm' ),
			__( 'Pressio CRM', 'pressio-crm' ),
			'pressio_crm_manage_contacts',
			'pressio-crm',
			[ $this, 'render_app' ],
			'none',  // icon injected via admin_head CSS — immune to WP active-state filter
			30
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Dashboard', 'pressio-crm' ),
			__( 'Dashboard', 'pressio-crm' ),
			'pressio_crm_manage_contacts',
			'pressio-crm',
			[ $this, 'render_app' ]
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Contacts', 'pressio-crm' ),
			__( 'Contacts', 'pressio-crm' ),
			'pressio_crm_manage_contacts',
			'pressio-crm-contacts',
			[ $this, 'render_app' ]
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Pipeline', 'pressio-crm' ),
			__( 'Pipeline', 'pressio-crm' ),
			'pressio_crm_manage_contacts',
			'pressio-crm-pipeline',
			[ $this, 'render_app' ]
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Tasks', 'pressio-crm' ),
			__( 'Tasks', 'pressio-crm' ),
			'pressio_crm_manage_contacts',
			'pressio-crm-tasks',
			[ $this, 'render_app' ]
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Reports', 'pressio-crm' ),
			__( 'Reports', 'pressio-crm' ),
			'pressio_crm_manage_contacts',
			'pressio-crm-reports',
			[ $this, 'render_app' ]
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Integrations', 'pressio-crm' ),
			__( 'Integrations', 'pressio-crm' ),
			'pressio_crm_manage_settings',
			'pressio-crm-integrations',
			[ $this, 'render_app' ]
		);

		add_submenu_page(
			'pressio-crm',
			__( 'Settings', 'pressio-crm' ),
			__( 'Settings', 'pressio-crm' ),
			'pressio_crm_manage_settings',
			'pressio-crm-settings',
			[ $this, 'render_app' ]
		);
	}

	public function render_app(): void {
		echo '<div id="pressio-crm-root"></div>';
	}

	/**
	 * Enqueue the Vue SPA — Vite dev server in dev mode, compiled build/ in production.
	 *
	 * @param string $hook_suffix Current admin page hook suffix.
	 */
	public function enqueue_assets( string $hook_suffix ): void {
		if ( ! $this->is_crm_page( $hook_suffix ) ) {
			return;
		}

		// Required for the WP media library picker used on Settings → Email Branding (logo upload).
		wp_enqueue_media();

		$config   = $this->get_config();
		// Dev mode is active when the .dev sentinel file exists (created by `npm run dev`,
		// deleted by `npm run build`). No manual config toggle needed.
		$is_dev   = file_exists( PRESSIO_CRM_PATH . '.dev' );
		$vite_url = rtrim( $config['vite_url'] ?? 'http://localhost:5173', '/' );

		// Vite produces ESM — add type="module" to our script tags.
		add_filter( 'script_loader_tag', [ $this, 'add_module_type' ], 10, 2 );

		// Build the window.pressioCrm data object used by the Vue app.
		$settings = get_option( 'pressio_crm_settings', [] );
		$settings = is_array( $settings ) ? $settings : [];
		$crm_data = wp_json_encode(
			[
				'restUrl'              => esc_url_raw( rest_url( 'pressio-crm/v1' ) ),
				'nonce'                => wp_create_nonce( 'wp_rest' ),
				'userId'               => get_current_user_id(),
				'adminEmail'           => sanitize_email( get_option( 'admin_email' ) ),
				'currency'             => sanitize_text_field( $settings['currency'] ?? 'USD' ),
				'dateFormat'           => sanitize_text_field( $settings['date_format'] ?? 'Y-m-d' ),
				'version'              => PRESSIO_CRM_VERSION,
				'adminUrl'             => esc_url( admin_url( 'admin.php' ) ),
				'assetUrl'             => esc_url( PRESSIO_CRM_URL . 'assets/' ),
				'page'                 => sanitize_key( wp_unslash( $_GET['page'] ?? 'pressio-crm' ) ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				'sampleDataLoaded'     => (bool) get_option( 'pressio_crm_sample_data_loaded', false ),
				'emailDefaultSubject'  => sanitize_text_field( $settings['email_default_subject'] ?? '' ),
				'emailDefaultBody'     => wp_kses_post( $settings['email_default_body'] ?? '' ),
			]
		);

		if ( $is_dev ) {
			// Dev mode — load from Vite dev server for HMR. No build step needed.
			// @vite/client enables hot module replacement in the browser.
			wp_enqueue_script(
				'pressio-crm-vite',
				$vite_url . '/@vite/client',
				[],
				null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
				false
			);

			wp_enqueue_script(
				'pressio-crm-admin',
				$vite_url . '/src/main.js',
				[ 'pressio-crm-vite' ],
				null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
				false
			);

			// Vite injects CSS via JS in dev mode — no separate stylesheet needed.
		} else {
			// Production — serve the compiled build/ files.
			$js_path = PRESSIO_CRM_PATH . 'build/index.js';
			if ( ! file_exists( $js_path ) ) {
				return;
			}

			// Use file modification time as version so every `npm run build` automatically
			// busts the browser cache — prevents stale chunk references after a rebuild.
			$js_ver = (string) filemtime( $js_path );

			wp_enqueue_script(
				'pressio-crm-admin',
				PRESSIO_CRM_URL . 'build/index.js',
				[ 'wp-tinymce', 'quicktags' ],
				$js_ver,
				true
			);

			$css_path = PRESSIO_CRM_PATH . 'build/index.css';
			if ( file_exists( $css_path ) ) {
				wp_enqueue_style(
					'pressio-crm-admin',
					PRESSIO_CRM_URL . 'build/index.css',
					[],
					(string) filemtime( $css_path )
				);

				// Remove WP admin's .wrap margins so our full-width layout sits flush.
				wp_add_inline_style(
					'pressio-crm-admin',
					'#wpbody-content > .wrap { margin: 0; padding: 0; max-width: none; }'
				);
			}
		}

		wp_add_inline_script( 'pressio-crm-admin', 'window.pressioCrm = ' . $crm_data, 'before' );
	}

	/**
	 * Load plugin config from config.php, falling back to safe defaults.
	 * config.php is gitignored — copy config.example.php to create it.
	 *
	 * @return array{ dev: bool, vite_url: string }
	 */
	private function get_config(): array {
		$config_file = PRESSIO_CRM_PATH . 'config.php';

		if ( file_exists( $config_file ) ) {
			$config = require $config_file; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
			if ( is_array( $config ) ) {
				return $config;
			}
		}

		return [
			'dev'      => false,
			'vite_url' => 'http://localhost:5173',
		];
	}

	/**
	 * Redirect to the onboarding flow on first activation.
	 * Runs once and self-clears the option so subsequent visits go straight to the dashboard.
	 *
	 * Hooked to current_screen so get_current_screen() is guaranteed non-null.
	 *
	 * @param WP_Screen $screen The current screen object.
	 */
	public function maybe_redirect_onboarding( WP_Screen $screen ): void {
		if ( ! get_option( 'pressio_crm_show_onboarding' ) ) {
			return;
		}

		// Only redirect when the user is landing on the top-level CRM page directly.
		if ( 'toplevel_page_pressio-crm' !== $screen->id ) {
			return;
		}

		delete_option( 'pressio_crm_show_onboarding' );

		wp_safe_redirect( admin_url( 'admin.php?page=pressio-crm#/onboarding' ) );
		exit;
	}

	/**
	 * Adds type="module" to the pressio-crm-admin script tag.
	 * Required so browsers treat the Vite ESM bundle as an ES module.
	 *
	 * @param string $tag    The full <script> HTML tag.
	 * @param string $handle Registered script handle.
	 */
	public function add_module_type( string $tag, string $handle ): string {
		if ( ! in_array( $handle, [ 'pressio-crm-admin', 'pressio-crm-vite' ], true ) ) {
			return $tag;
		}

		return str_replace( ' src=', ' type="module" src=', $tag );
	}

	/**
	 * Inject the coloured menu icon via admin_head (global — the menu is visible
	 * on every admin page).  Using background-image on the wrapper prevents
	 * WordPress from applying its active-state filter (brightness/invert) which
	 * would otherwise turn the icon white when the item is selected.
	 */
	public function enqueue_menu_icon_css(): void {
		$svg = "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E"
			. "%3Crect width='20' height='20' rx='4' fill='%236366f1'/%3E"
			. "%3Crect x='2.5' y='3' width='4' height='14' rx='1.2' fill='%23ffffff'/%3E"
			. "%3Crect x='8' y='6' width='4' height='11' rx='1.2' fill='%23ffffff' opacity='0.75'/%3E"
			. "%3Crect x='13.5' y='9' width='4' height='8' rx='1.2' fill='%23ffffff' opacity='0.55'/%3E"
			. '%3C/svg%3E';
		// URL-encoded (not base64) so WordPress's svg-painter.js does not detect
		// this as a base64 SVG and repaint all fills with the color-scheme color.
		// svg-painter checks for 'data:image/svg+xml;base64' — this URI uses no base64.
		// Attributes single-quoted: the CSS url() wrapper uses double quotes.
		// esc_url() strips data: URIs, so we echo directly; SVG is hardcoded, no user input.
		// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
		echo '<style>'
			. '#toplevel_page_pressio-crm .wp-menu-image{'
			. 'background-image:url("data:image/svg+xml,' . $svg . '")!important;'
			. 'background-repeat:no-repeat!important;'
			. 'background-position:center!important;'
			. 'background-size:20px 20px!important}'
			. '#toplevel_page_pressio-crm .wp-menu-image img{display:none!important}'
			. '#toplevel_page_pressio-crm .wp-menu-image::before{display:none!important}'
			. '</style>';
		// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Remove all admin notices on Pressio CRM pages — third-party notices
	 * break the full-screen SPA layout and are irrelevant in context.
	 */
	public function suppress_notices(): void {
		$screen = get_current_screen();
		if ( $screen && $this->is_crm_page( $screen->id ) ) {
			remove_all_actions( 'admin_notices' );
			remove_all_actions( 'all_admin_notices' );
		}
	}

	/**
	 * Returns true when $hook_suffix belongs to one of our admin pages.
	 *
	 * @param string $hook_suffix Value passed by admin_enqueue_scripts.
	 */
	private function is_crm_page( string $hook_suffix ): bool {
		$crm_hooks = [
			'toplevel_page_pressio-crm',
			'pressio-crm_page_pressio-crm-contacts',
			'pressio-crm_page_pressio-crm-pipeline',
			'pressio-crm_page_pressio-crm-tasks',
			'pressio-crm_page_pressio-crm-reports',
			'pressio-crm_page_pressio-crm-integrations',
			'pressio-crm_page_pressio-crm-settings',
		];

		return in_array( $hook_suffix, $crm_hooks, true );
	}
}
