<?php

declare( strict_types=1 );

if ( ! defined( 'ABSPATH' ) ) exit;

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

/**
 * Public REST routes for customer operations.
 * POST /customer/lookup   — name/email for returning-customer auto-fill
 * PUT  /customer/profile  — update profile (password); requires JWT
 * GET  /customer/profile  — get own profile; requires JWT
 */
class Playard_API_Customers {

	private Playard_Customer $customer;

	public function __construct( Playard_Customer $customer ) {
		$this->customer = $customer;
	}

	public function register_routes(): void {
		$namespace = 'playard/v1';

		register_rest_route( $namespace, '/customer/lookup', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'lookup' ],
			// Public: returns only masked data (first 2 chars of name, masked email).
			'permission_callback' => '__return_true',
			'args'                => [
				'phone' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
			],
		] );

		register_rest_route( $namespace, '/customer/profile', [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_profile' ],
				'permission_callback' => '__return_true',
			],
			[
				'methods'             => 'PUT',
				'callback'            => [ $this, 'update_profile' ],
				'permission_callback' => '__return_true',
			],
		] );

		register_rest_route( $namespace, '/customer/profile/photo', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'upload_photo' ],
			'permission_callback' => '__return_true',
		] );
	}

	// ── Lookup (unauthenticated) ──────────────────────────────────────────────────

	public function lookup( WP_REST_Request $request ): WP_REST_Response {
		$phone  = sanitize_text_field( (string) $request->get_param( 'phone' ) );
		$result = $this->customer->lookup_by_phone( $phone );

		if ( ! $result ) {
			return new WP_REST_Response(
				playard_rest_error( 'not_found', __( 'No account found for this phone number.', 'playard' ) ),
				404
			);
		}

		return new WP_REST_Response( playard_rest_success( $result ), 200 );
	}

	// ── Profile — GET ─────────────────────────────────────────────────────────────

	public function get_profile( WP_REST_Request $request ): WP_REST_Response {
		$customer_id = $this->authenticate( $request );

		if ( ! $customer_id ) {
			return new WP_REST_Response(
				playard_rest_error( 'unauthorized', __( 'Authentication required.', 'playard' ) ),
				401
			);
		}

		$row = $this->customer->get( $customer_id );

		if ( ! $row ) {
			return new WP_REST_Response(
				playard_rest_error( 'not_found', __( 'Customer not found.', 'playard' ) ),
				404
			);
		}

		return new WP_REST_Response( playard_rest_success( $this->format_customer( $row ) ), 200 );
	}

	// ── Profile — PUT ─────────────────────────────────────────────────────────────

	public function update_profile( WP_REST_Request $request ): WP_REST_Response {
		$customer_id = $this->authenticate( $request );

		if ( ! $customer_id ) {
			return new WP_REST_Response(
				playard_rest_error( 'unauthorized', __( 'Authentication required.', 'playard' ) ),
				401
			);
		}

		$data    = (array) $request->get_json_params();
		$updated = [];

		global $wpdb;
		$db_updates = [];
		$formats    = [];

		// Name
		if ( isset( $data['name'] ) ) {
			$name = sanitize_text_field( (string) $data['name'] );
			if ( strlen( $name ) < 2 ) {
				return new WP_REST_Response(
					playard_rest_error( 'validation_error', __( 'Name must be at least 2 characters.', 'playard' ) ),
					400
				);
			}
			$db_updates['name'] = $name;
			$formats[]          = '%s';
			$updated[]          = 'name';
		}

		// Phone — optional; empty string clears the field
		if ( array_key_exists( 'phone', $data ) ) {
			$phone = sanitize_text_field( (string) $data['phone'] );
			if ( ! empty( $phone ) ) {
				$taken = $wpdb->get_var( $wpdb->prepare(
					"SELECT id FROM {$wpdb->prefix}playard_customers WHERE phone = %s AND id != %d LIMIT 1",
					$phone, $customer_id
				) );
				if ( $taken ) {
					return new WP_REST_Response(
						playard_rest_error( 'phone_taken', __( 'This phone number is already registered.', 'playard' ) ),
						422
					);
				}
			}
			$db_updates['phone'] = $phone;
			$formats[]           = '%s';
			$updated[]           = 'phone';
		}

		// Password set / change
		if ( ! empty( $data['new_password'] ) ) {
			$row = $this->customer->get( $customer_id );
			if ( ! $row ) {
				return new WP_REST_Response(
					playard_rest_error( 'not_found', __( 'Customer not found.', 'playard' ) ),
					404
				);
			}

			if ( ! empty( $row->password_hash ) ) {
				// Password already set — current password is required to change it
				if ( empty( $data['current_password'] ) || ! wp_check_password( (string) $data['current_password'], $row->password_hash ) ) {
					return new WP_REST_Response(
						playard_rest_error( 'wrong_password', __( 'Current password is incorrect.', 'playard' ) ),
						422
					);
				}
			}
			// else: no password set yet — allow first-time set without current password

			$result = $this->customer->set_password( $customer_id, (string) $data['new_password'] );
			if ( is_wp_error( $result ) ) {
				return new WP_REST_Response(
					playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
					422
				);
			}
			$updated[] = 'password';
		}

		if ( empty( $db_updates ) && ! in_array( 'password', $updated, true ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'validation_error', __( 'No changes provided.', 'playard' ) ),
				400
			);
		}

		if ( ! empty( $db_updates ) ) {
			$wpdb->update(
				$wpdb->prefix . 'playard_customers',
				$db_updates,
				[ 'id' => $customer_id ],
				$formats,
				[ '%d' ]
			);
		}

		do_action( 'playard_customer_profile_updated', $customer_id, $updated );

		$row = $this->customer->get( $customer_id );
		return new WP_REST_Response(
			playard_rest_success( [ 'customer' => $row ? $this->format_customer( $row ) : [] ], __( 'Profile updated.', 'playard' ) ),
			200
		);
	}

	// ── Profile — Photo upload ────────────────────────────────────────────────────

	public function upload_photo( WP_REST_Request $request ): WP_REST_Response {
		$customer_id = $this->authenticate( $request );

		if ( ! $customer_id ) {
			return new WP_REST_Response(
				playard_rest_error( 'unauthorized', __( 'Authentication required.', 'playard' ) ),
				401
			);
		}

		$files = $request->get_file_params();

		if ( empty( $files['photo'] ) || $files['photo']['error'] !== UPLOAD_ERR_OK ) {
			return new WP_REST_Response(
				playard_rest_error( 'no_file', __( 'No photo uploaded.', 'playard' ) ),
				400
			);
		}

		$file          = $files['photo'];
		$allowed_types = [ 'image/jpeg', 'image/png', 'image/gif', 'image/webp' ];
		$finfo         = new finfo( FILEINFO_MIME_TYPE );
		$mime          = $finfo->file( $file['tmp_name'] );

		if ( ! in_array( $mime, $allowed_types, true ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_type', __( 'Only JPEG, PNG, GIF, and WebP images are allowed.', 'playard' ) ),
				400
			);
		}

		if ( $file['size'] > 2 * 1024 * 1024 ) {
			return new WP_REST_Response(
				playard_rest_error( 'file_too_large', __( 'Photo must be under 2 MB.', 'playard' ) ),
				400
			);
		}

		$upload_dir = wp_upload_dir();
		$dir        = trailingslashit( $upload_dir['basedir'] ) . 'playard/avatars';

		if ( ! wp_mkdir_p( $dir ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'upload_failed', __( 'Could not create upload directory.', 'playard' ) ),
				500
			);
		}

		$ext  = $mime === 'image/jpeg' ? 'jpg' : str_replace( 'image/', '', $mime );
		$file['name'] = 'customer-' . $customer_id . '.' . $ext;

		$playard_dest_dir = $dir;
		$playard_base_url = trailingslashit( $upload_dir['baseurl'] ) . 'playard/avatars';

		$upload_dir_filter = static function( $dirs ) use ( $playard_dest_dir, $playard_base_url ) {
			$dirs['path']   = $playard_dest_dir;
			$dirs['url']    = $playard_base_url;
			$dirs['subdir'] = '';
			return $dirs;
		};
		add_filter( 'upload_dir', $upload_dir_filter );

		$moved = wp_handle_sideload(
			$file,
			[
				'test_form' => false,
				'mimes'     => [
					'jpg|jpeg|jpe' => 'image/jpeg',
					'png'          => 'image/png',
					'gif'          => 'image/gif',
					'webp'         => 'image/webp',
				],
			]
		);

		remove_filter( 'upload_dir', $upload_dir_filter );

		if ( ! empty( $moved['error'] ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'upload_failed', __( 'Could not save photo.', 'playard' ) ),
				500
			);
		}

		$url = $moved['url'];

		global $wpdb;
		$wpdb->update(
			$wpdb->prefix . 'playard_customers',
			[ 'avatar_url' => esc_url_raw( $url ) ],
			[ 'id'         => $customer_id ],
			[ '%s' ],
			[ '%d' ]
		);

		return new WP_REST_Response( playard_rest_success( [ 'avatar_url' => $url ] ), 200 );
	}

	// ── Helpers ───────────────────────────────────────────────────────────────────

	/**
	 * @param object $row  Customer DB row.
	 * @return array<string,mixed>
	 */
	private function format_customer( object $row ): array {
		global $wpdb;

		$today    = current_time( 'Y-m-d' );
		$customer_id = (int) $row->id;

		$upcoming_count = (int) $wpdb->get_var( $wpdb->prepare(
			"SELECT COUNT(*) FROM {$wpdb->prefix}playard_bookings
			 WHERE customer_id = %d AND booking_date >= %s AND booking_status IN ('confirmed','pending')",
			$customer_id, $today
		) );

		$cancelled_count = (int) $wpdb->get_var( $wpdb->prepare(
			"SELECT COUNT(*) FROM {$wpdb->prefix}playard_bookings
			 WHERE customer_id = %d AND booking_status = 'cancelled'",
			$customer_id
		) );

		$completed_count = (int) $wpdb->get_var( $wpdb->prepare(
			"SELECT COUNT(*) FROM {$wpdb->prefix}playard_bookings
			 WHERE customer_id = %d AND booking_status = 'completed'",
			$customer_id
		) );

		$last_booking_date = (string) $wpdb->get_var( $wpdb->prepare(
			"SELECT MAX(booking_date) FROM {$wpdb->prefix}playard_bookings
			 WHERE customer_id = %d AND booking_status != 'cancelled'",
			$customer_id
		) );

		return [
			'id'               => $customer_id,
			'name'             => (string) $row->name,
			'phone'            => (string) $row->phone,
			'email'            => (string) ( $row->email ?? '' ),
			'avatar_url'       => esc_url_raw( (string) ( $row->avatar_url ?? '' ) ),
			'has_password'     => ! empty( $row->password_hash ),
			'total_bookings'   => (int) $row->total_bookings,
			'total_spent'      => (float) $row->total_spent,
			'upcoming_bookings'  => $upcoming_count,
			'cancelled_bookings' => $cancelled_count,
			'completed_bookings' => $completed_count,
			'last_booking_date'  => $last_booking_date ?: '',
			'created_at'         => (string) ( $row->created_at ?? '' ),
		];
	}

	// ── JWT auth helper ───────────────────────────────────────────────────────────

	/**
	 * Extracts and validates the customer JWT from the Authorization header.
	 * Returns the customer ID or 0 if invalid / missing.
	 */
	private function authenticate( WP_REST_Request $request ): int {
		$header = (string) $request->get_header( 'authorization' );

		if ( ! $header || ! str_starts_with( $header, 'Bearer ' ) ) {
			return 0;
		}

		$token = trim( substr( $header, 7 ) );

		return (int) $this->customer->validate_jwt( $token );
	}
}
