<?php
/**
 * Customers admin screen: menu registration, page router, and write handlers.
 *
 * Routes between the list, add form, edit form, and read-only view. Create,
 * update, and archive are nonce-verified, capability-gated, sanitize every
 * field, and use Post/Redirect/Get on success. Validation errors re-render the
 * form with the submitted values preserved.
 *
 * @package WrenchPress
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class WrenchPress_Customers_Admin_Page
 */
class WrenchPress_Customers_Admin_Page {

	/**
	 * Admin page slug.
	 */
	const MENU_SLUG = 'wrenchpress-customers';

	/**
	 * Nonce action for the create form.
	 */
	const CREATE_NONCE = 'wrenchpress_create_customer';

	/**
	 * Nonce action for the edit form.
	 */
	const EDIT_NONCE = 'wrenchpress_edit_customer';

	/**
	 * Validation error messages collected during the current request.
	 *
	 * @var string[]
	 */
	private static $form_errors = array();

	/**
	 * Submitted field values to re-populate the form after a validation error.
	 *
	 * @var array<string,string>
	 */
	private static $form_values = array();

	/**
	 * Customer id being edited when an update validation error occurs. Zero for
	 * the create flow.
	 *
	 * @var int
	 */
	private static $edit_id = 0;

	/**
	 * Dispatch write actions on page load (before output).
	 *
	 * Archive is a nonce-protected row-action link (GET); create and update are
	 * POST submissions.
	 *
	 * @return void
	 */
	public static function handle_load() {
		$get_action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce checked in handle_archive().

		if ( 'archive' === $get_action ) {
			self::handle_archive();
			return;
		}

		$method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : '';

		if ( 'POST' !== $method ) {
			return;
		}

		$post_action = isset( $_POST['wrenchpress_action'] ) ? sanitize_key( wp_unslash( $_POST['wrenchpress_action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked in the specific handler below.

		if ( 'create' === $post_action ) {
			self::handle_create();
		} elseif ( 'update' === $post_action ) {
			self::handle_update();
		}
	}

	/**
	 * Read and sanitize the six customer fields from the POST body.
	 *
	 * @return array<string,string>
	 */
	private static function read_posted_fields() {
		// phpcs:disable WordPress.Security.NonceVerification.Missing -- callers verify the nonce before invoking this.
		return array(
			'first_name' => isset( $_POST['first_name'] ) ? sanitize_text_field( wp_unslash( $_POST['first_name'] ) ) : '',
			'last_name'  => isset( $_POST['last_name'] ) ? sanitize_text_field( wp_unslash( $_POST['last_name'] ) ) : '',
			'phone'      => isset( $_POST['phone'] ) ? sanitize_text_field( wp_unslash( $_POST['phone'] ) ) : '',
			'email'      => isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '',
			'address'    => isset( $_POST['address'] ) ? sanitize_text_field( wp_unslash( $_POST['address'] ) ) : '',
			'notes'      => isset( $_POST['notes'] ) ? sanitize_textarea_field( wp_unslash( $_POST['notes'] ) ) : '',
		);
		// phpcs:enable WordPress.Security.NonceVerification.Missing
	}

	/**
	 * Validate and insert a new customer (PRG on success).
	 *
	 * @return void
	 */
	private static function handle_create() {
		check_admin_referer( self::CREATE_NONCE );

		if ( ! current_user_can( WrenchPress_Capabilities::CREATE_CUSTOMERS ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'pitdesk-repair-crm' ) );
		}

		$values = self::read_posted_fields();
		$errors = self::validate( $values );

		if ( ! empty( $errors ) ) {
			self::$form_errors = $errors;
			self::$form_values = $values;
			return;
		}

		WrenchPress_Customers_Repository::create( $values );

		self::redirect_to_list( 'created' );
	}

	/**
	 * Validate and update an existing customer (PRG on success).
	 *
	 * @return void
	 */
	private static function handle_update() {
		check_admin_referer( self::EDIT_NONCE );

		if ( ! current_user_can( WrenchPress_Capabilities::EDIT_CUSTOMERS ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'pitdesk-repair-crm' ) );
		}

		$id = isset( $_POST['customer'] ) ? absint( wp_unslash( $_POST['customer'] ) ) : 0;

		$values = self::read_posted_fields();
		$errors = self::validate( $values );

		if ( 0 === $id ) {
			$errors[] = __( 'That customer could not be found.', 'pitdesk-repair-crm' );
		}

		if ( ! empty( $errors ) ) {
			self::$form_errors = $errors;
			self::$form_values = $values;
			self::$edit_id     = $id;
			return;
		}

		WrenchPress_Customers_Repository::update( $id, $values );

		self::redirect_to_list( 'updated' );
	}

	/**
	 * Verify the per-row nonce and soft-delete (archive) a customer (PRG).
	 *
	 * @return void
	 */
	private static function handle_archive() {
		$id = isset( $_GET['customer'] ) ? absint( wp_unslash( $_GET['customer'] ) ) : 0;

		check_admin_referer( 'wrenchpress_archive_customer_' . $id );

		if ( ! current_user_can( WrenchPress_Capabilities::DELETE_CUSTOMERS ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'pitdesk-repair-crm' ) );
		}

		if ( $id > 0 ) {
			WrenchPress_Customers_Repository::archive( $id );
		}

		self::redirect_to_list( 'archived' );
	}

	/**
	 * Redirect to the list with a success flag and exit (PRG).
	 *
	 * @param string $flag Query flag: created, updated, or archived.
	 * @return void
	 */
	private static function redirect_to_list( $flag ) {
		wp_safe_redirect(
			add_query_arg(
				array(
					'page' => self::MENU_SLUG,
					$flag  => 1,
				),
				admin_url( 'admin.php' )
			)
		);
		exit;
	}

	/**
	 * Server-side validation for the customer form.
	 *
	 * @param array<string,string> $values Sanitized field values.
	 * @return string[] Error messages (empty when valid).
	 */
	private static function validate( array $values ) {
		$errors = array();

		if ( '' === $values['first_name'] ) {
			$errors[] = __( 'First name is required.', 'pitdesk-repair-crm' );
		}

		if ( '' === $values['last_name'] ) {
			$errors[] = __( 'Last name is required.', 'pitdesk-repair-crm' );
		}

		if ( '' === $values['phone'] ) {
			$errors[] = __( 'Phone is required.', 'pitdesk-repair-crm' );
		}

		if ( '' !== $values['email'] && ! is_email( $values['email'] ) ) {
			$errors[] = __( 'Enter a valid email address, or leave the field blank.', 'pitdesk-repair-crm' );
		}

		return $errors;
	}

	/**
	 * Route the page to the list, add form, edit form, or read-only view.
	 *
	 * @return void
	 */
	public static function render() {
		if ( ! current_user_can( WrenchPress_Capabilities::VIEW_CUSTOMERS ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'pitdesk-repair-crm' ) );
		}

		$action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only view routing.

		if ( 'view' === $action ) {
			self::render_view();
			return;
		}

		if ( 'edit' === $action || self::$edit_id > 0 ) {
			self::render_edit_form();
			return;
		}

		if ( 'add' === $action || ! empty( self::$form_errors ) ) {
			self::render_add_form();
			return;
		}

		self::render_list();
	}

	/**
	 * Render the customers list screen.
	 *
	 * @return void
	 */
	private static function render_list() {
		$list_table = new WrenchPress_Customers_List_Table();
		$list_table->prepare_items();

		$add_url = add_query_arg(
			array(
				'page'   => self::MENU_SLUG,
				'action' => 'add',
			),
			admin_url( 'admin.php' )
		);

		echo '<div class="wrap">';
		echo '<h1 class="wp-heading-inline">' . esc_html__( 'Customers', 'pitdesk-repair-crm' ) . '</h1>';

		if ( current_user_can( WrenchPress_Capabilities::CREATE_CUSTOMERS ) ) {
			echo ' <a href="' . esc_url( $add_url ) . '" class="page-title-action">' . esc_html__( 'Add Customer', 'pitdesk-repair-crm' ) . '</a>';
		}

		echo '<hr class="wp-header-end">';

		self::maybe_render_success_notice();

		echo '<form method="get">';
		echo '<input type="hidden" name="page" value="' . esc_attr( self::MENU_SLUG ) . '" />';
		$list_table->search_box( __( 'Search Customers', 'pitdesk-repair-crm' ), 'wrenchpress-customer-search' );
		$list_table->display();
		echo '</form>';

		echo '</div>';
	}

	/**
	 * Render the success admin notice for the PRG flags.
	 *
	 * @return void
	 */
	private static function maybe_render_success_notice() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- success flags come from our own PRG redirects.
		if ( isset( $_GET['created'] ) ) {
			$message = __( 'Customer added.', 'pitdesk-repair-crm' );
		} elseif ( isset( $_GET['updated'] ) ) {
			$message = __( 'Customer updated.', 'pitdesk-repair-crm' );
		} elseif ( isset( $_GET['archived'] ) ) {
			$message = __( 'Customer archived.', 'pitdesk-repair-crm' );
		} else {
			return;
		}
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $message ) . '</p></div>';
	}

	/**
	 * Render the add-customer form.
	 *
	 * @return void
	 */
	private static function render_add_form() {
		if ( ! current_user_can( WrenchPress_Capabilities::CREATE_CUSTOMERS ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'pitdesk-repair-crm' ) );
		}

		$values = self::form_values_with_defaults( self::$form_values );

		self::render_form( false, 0, $values );
	}

	/**
	 * Render the edit-customer form, prefilled from the record (or from the
	 * preserved values after a validation error).
	 *
	 * @return void
	 */
	private static function render_edit_form() {
		if ( ! current_user_can( WrenchPress_Capabilities::EDIT_CUSTOMERS ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'pitdesk-repair-crm' ) );
		}

		if ( self::$edit_id > 0 ) {
			$id     = self::$edit_id;
			$values = self::form_values_with_defaults( self::$form_values );
		} else {
			$id       = isset( $_GET['customer'] ) ? absint( wp_unslash( $_GET['customer'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only prefill of an edit form.
			$customer = $id > 0 ? WrenchPress_Customers_Repository::get( $id ) : null;

			if ( ! $customer ) {
				echo '<div class="wrap"><h1 class="wp-heading-inline">' . esc_html__( 'Edit Customer', 'pitdesk-repair-crm' ) . '</h1>';
				echo '<hr class="wp-header-end">';
				echo '<div class="notice notice-error"><p>' . esc_html__( 'That customer could not be found.', 'pitdesk-repair-crm' ) . '</p></div>';
				echo '<p><a href="' . esc_url( self::list_url() ) . '" class="button">' . esc_html__( 'Back to Customers', 'pitdesk-repair-crm' ) . '</a></p></div>';
				return;
			}

			$values = self::form_values_with_defaults(
				array(
					'first_name' => $customer->first_name,
					'last_name'  => $customer->last_name,
					'phone'      => $customer->phone,
					'email'      => $customer->email,
					'address'    => $customer->address,
					'notes'      => $customer->notes,
				)
			);
		}

		self::render_form( true, $id, $values );
	}

	/**
	 * Render a read-only detail view of a single customer.
	 *
	 * @return void
	 */
	private static function render_view() {
		$id       = isset( $_GET['customer'] ) ? absint( wp_unslash( $_GET['customer'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only view.
		$customer = $id > 0 ? WrenchPress_Customers_Repository::get( $id ) : null;

		echo '<div class="wrap">';
		echo '<h1 class="wp-heading-inline">' . esc_html__( 'View Customer', 'pitdesk-repair-crm' ) . '</h1>';

		if ( $customer && current_user_can( WrenchPress_Capabilities::EDIT_CUSTOMERS ) ) {
			$edit_url = add_query_arg(
				array(
					'page'     => self::MENU_SLUG,
					'action'   => 'edit',
					'customer' => (int) $customer->id,
				),
				admin_url( 'admin.php' )
			);
			echo ' <a href="' . esc_url( $edit_url ) . '" class="page-title-action">' . esc_html__( 'Edit', 'pitdesk-repair-crm' ) . '</a>';
		}

		if ( $customer && current_user_can( WrenchPress_Capabilities::MANAGE_APPOINTMENTS ) ) {
			$schedule_url = add_query_arg(
				array(
					'page'     => 'wrenchpress-appointments',
					'customer' => (int) $customer->id,
				),
				admin_url( 'admin.php' )
			);
			echo ' <a href="' . esc_url( $schedule_url ) . '" class="page-title-action">' . esc_html__( 'Schedule appointment', 'pitdesk-repair-crm' ) . '</a>';
		}

		echo '<hr class="wp-header-end">';

		if ( ! $customer ) {
			echo '<div class="notice notice-error"><p>' . esc_html__( 'That customer could not be found.', 'pitdesk-repair-crm' ) . '</p></div>';
			echo '<p><a href="' . esc_url( self::list_url() ) . '" class="button">' . esc_html__( 'Back to Customers', 'pitdesk-repair-crm' ) . '</a></p></div>';
			return;
		}

		$rows = array(
			__( 'First name', 'pitdesk-repair-crm' )   => $customer->first_name,
			__( 'Last name', 'pitdesk-repair-crm' )    => $customer->last_name,
			__( 'Phone', 'pitdesk-repair-crm' )        => $customer->phone,
			__( 'Email', 'pitdesk-repair-crm' )        => $customer->email,
			__( 'Address', 'pitdesk-repair-crm' )      => $customer->address,
			__( 'Notes', 'pitdesk-repair-crm' )        => $customer->notes,
			__( 'Date added', 'pitdesk-repair-crm' )   => self::format_datetime( $customer->created_at ),
			__( 'Last updated', 'pitdesk-repair-crm' ) => self::format_datetime( $customer->updated_at ),
		);

		echo '<table class="form-table" role="presentation"><tbody>';
		foreach ( $rows as $label => $value ) {
			echo '<tr><th scope="row">' . esc_html( $label ) . '</th><td>' . nl2br( esc_html( (string) $value ) ) . '</td></tr>';
		}
		echo '</tbody></table>';

		self::render_customer_360( (int) $customer->id );

		echo '<p><a href="' . esc_url( self::list_url() ) . '" class="button">' . esc_html__( 'Back to Customers', 'pitdesk-repair-crm' ) . '</a></p>';
		echo '</div>';
	}

	/**
	 * Render the read-only customer 360 blocks (D-06): the customer's vehicles and
	 * recent work orders as escaped summary cross-links out to the standalone
	 * Vehicles and Work Orders screens. There are no CRUD controls here; each block
	 * shows an empty state when there is nothing to list.
	 *
	 * @param int $customer_id Customer id.
	 * @return void
	 */
	private static function render_customer_360( $customer_id ) {
		$customer_id = (int) $customer_id;
		$base        = admin_url( 'admin.php' );

		// Vehicles block.
		$vehicles = WrenchPress_Vehicles_Repository::list_active(
			array(
				'customer_id' => $customer_id,
				'per_page'    => 100,
			)
		);

		echo '<h2>' . esc_html__( 'Vehicles', 'pitdesk-repair-crm' ) . '</h2>';

		if ( empty( $vehicles ) ) {
			echo '<p>' . esc_html__( 'No vehicles on file for this customer.', 'pitdesk-repair-crm' ) . '</p>';
		} else {
			echo '<ul>';
			foreach ( $vehicles as $vehicle ) {
				$view_url = add_query_arg(
					array(
						'page'    => 'wrenchpress-vehicles',
						'action'  => 'view',
						'vehicle' => (int) $vehicle->id,
					),
					$base
				);
				echo '<li><a href="' . esc_url( $view_url ) . '">' . esc_html( WrenchPress_Vehicles_List_Table::vehicle_label( $vehicle ) ) . '</a></li>';
			}
			echo '</ul>';
		}

		// Recent work orders block.
		$work_orders = WrenchPress_Work_Orders_Repository::list_for_customer( $customer_id, 10 );
		$statuses    = WrenchPress_Work_Orders_Repository::statuses();

		echo '<h2>' . esc_html__( 'Recent work orders', 'pitdesk-repair-crm' ) . '</h2>';

		if ( empty( $work_orders ) ) {
			echo '<p>' . esc_html__( 'No work orders yet for this customer.', 'pitdesk-repair-crm' ) . '</p>';
			return;
		}

		echo '<ul>';
		foreach ( $work_orders as $work_order ) {
			$wo_id    = (int) $work_order->id;
			$slug     = (string) $work_order->status;
			$label    = isset( $statuses[ $slug ] ) ? $statuses[ $slug ] : $slug;
			$view_url = add_query_arg(
				array(
					'page'       => 'wrenchpress-work-orders',
					'action'     => 'view',
					'work_order' => $wo_id,
				),
				$base
			);
			echo '<li><a href="' . esc_url( $view_url ) . '">' . esc_html( '#' . $wo_id ) . '</a> ';
			echo '<span class="wrenchpress-status wrenchpress-status--' . esc_attr( $slug ) . '">' . esc_html( $label ) . '</span></li>';
		}
		echo '</ul>';
	}

	/**
	 * Shared add/edit form renderer.
	 *
	 * @param bool                 $is_edit     Whether this is the edit flow.
	 * @param int                  $customer_id Customer id (edit flow only).
	 * @param array<string,string> $values      Field values to prefill.
	 * @return void
	 */
	private static function render_form( $is_edit, $customer_id, array $values ) {
		$list_url     = self::list_url();
		$title        = $is_edit ? __( 'Edit Customer', 'pitdesk-repair-crm' ) : __( 'Add Customer', 'pitdesk-repair-crm' );
		$submit_label = $is_edit ? __( 'Save Changes', 'pitdesk-repair-crm' ) : __( 'Add Customer', 'pitdesk-repair-crm' );
		$nonce_action = $is_edit ? self::EDIT_NONCE : self::CREATE_NONCE;
		$action_value = $is_edit ? 'update' : 'create';

		echo '<div class="wrap">';
		echo '<h1 class="wp-heading-inline">' . esc_html( $title ) . '</h1>';
		echo '<hr class="wp-header-end">';

		if ( ! empty( self::$form_errors ) ) {
			echo '<div class="notice notice-error"><ul>';
			foreach ( self::$form_errors as $error ) {
				echo '<li>' . esc_html( $error ) . '</li>';
			}
			echo '</ul></div>';
		}

		echo '<form method="post" action="' . esc_url( $list_url ) . '">';
		wp_nonce_field( $nonce_action );
		echo '<input type="hidden" name="wrenchpress_action" value="' . esc_attr( $action_value ) . '" />';

		if ( $is_edit ) {
			echo '<input type="hidden" name="customer" value="' . esc_attr( (string) $customer_id ) . '" />';
		}

		echo '<table class="form-table" role="presentation"><tbody>';
		self::text_row( 'first_name', __( 'First name', 'pitdesk-repair-crm' ), $values['first_name'], true );
		self::text_row( 'last_name', __( 'Last name', 'pitdesk-repair-crm' ), $values['last_name'], true );
		self::text_row( 'phone', __( 'Phone', 'pitdesk-repair-crm' ), $values['phone'], true, 'tel' );
		self::text_row( 'email', __( 'Email', 'pitdesk-repair-crm' ), $values['email'], false, 'email' );
		self::text_row( 'address', __( 'Address', 'pitdesk-repair-crm' ), $values['address'], false );
		self::textarea_row( 'notes', __( 'Notes', 'pitdesk-repair-crm' ), $values['notes'] );
		echo '</tbody></table>';

		echo '<p class="submit">';
		echo '<button type="submit" class="button button-primary">' . esc_html( $submit_label ) . '</button> ';
		echo '<a href="' . esc_url( $list_url ) . '" class="button button-link">' . esc_html__( 'Back to Customers', 'pitdesk-repair-crm' ) . '</a>';
		echo '</p>';

		echo '</form>';
		echo '</div>';
	}

	/**
	 * Merge submitted/loaded values over the empty-field defaults.
	 *
	 * @param array<string,string> $values Partial values.
	 * @return array<string,string>
	 */
	private static function form_values_with_defaults( array $values ) {
		return wp_parse_args(
			$values,
			array(
				'first_name' => '',
				'last_name'  => '',
				'phone'      => '',
				'email'      => '',
				'address'    => '',
				'notes'      => '',
			)
		);
	}

	/**
	 * URL of the Customers list screen.
	 *
	 * @return string
	 */
	private static function list_url() {
		return add_query_arg( array( 'page' => self::MENU_SLUG ), admin_url( 'admin.php' ) );
	}

	/**
	 * Format a stored datetime for display, or return an empty string.
	 *
	 * @param string $value Stored datetime.
	 * @return string
	 */
	private static function format_datetime( $value ) {
		$timestamp = strtotime( (string) $value );

		if ( ! $timestamp ) {
			return '';
		}

		return date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp );
	}

	/**
	 * Render one text input row in the form table.
	 *
	 * @param string $name     Field name.
	 * @param string $label    Visible label.
	 * @param string $value    Current value.
	 * @param bool   $required Whether the field is required.
	 * @param string $type     Input type attribute.
	 * @return void
	 */
	private static function text_row( $name, $label, $value, $required, $type = 'text' ) {
		$id = 'wrenchpress_' . $name;

		echo '<tr><th scope="row"><label for="' . esc_attr( $id ) . '">' . esc_html( $label );

		if ( $required ) {
			echo ' <span class="description">' . esc_html__( '(required)', 'pitdesk-repair-crm' ) . '</span>';
		}

		echo '</label></th><td>';
		printf(
			'<input name="%1$s" id="%2$s" type="%3$s" value="%4$s" class="regular-text"%5$s />',
			esc_attr( $name ),
			esc_attr( $id ),
			esc_attr( $type ),
			esc_attr( $value ),
			$required ? ' required' : ''
		);
		echo '</td></tr>';
	}

	/**
	 * Render the notes textarea row.
	 *
	 * @param string $name  Field name.
	 * @param string $label Visible label.
	 * @param string $value Current value.
	 * @return void
	 */
	private static function textarea_row( $name, $label, $value ) {
		$id = 'wrenchpress_' . $name;

		echo '<tr><th scope="row"><label for="' . esc_attr( $id ) . '">' . esc_html( $label ) . '</label></th><td>';
		printf(
			'<textarea name="%1$s" id="%2$s" rows="5" class="large-text">%3$s</textarea>',
			esc_attr( $name ),
			esc_attr( $id ),
			esc_textarea( $value )
		);
		echo '</td></tr>';
	}
}
