<?php

namespace SugarCalendarBookings\Customers;

use Exception;

/**
 * Customer logic for Customer model.
 *
 * @since 1.0.0
 */
class CustomerService {

	/**
	 * Create a new Customer.
	 *
	 * @since 1.0.0
	 *
	 * @param CreateCustomerDTO $dto Data for customer creation.
	 *
	 * @return CustomerModel The created Customer model.
	 *
	 * @throws Exception On customer creation failure.
	 */
	public function create( $dto ) {

		$this->validate_customer_exists( $dto );

		$customer = new CustomerModel();

		$this->fill_customer( $customer, $dto );

		$saved = $customer->save();

		if ( ! $saved ) {
			throw new Exception( esc_html__( 'Failed to create customer.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$this->save_relations( $customer, $dto );

		return $customer;
	}

	/**
	 * Update an existing Customer.
	 *
	 * @since 1.0.0
	 *
	 * @param CustomerModel $customer The customer model to update.
	 * @param CustomerDTO   $dto      Data for customer update.
	 *
	 * @return CustomerModel The updated Customer model.
	 *
	 * @throws Exception On customer update failure.
	 */
	public function update( $customer, $dto ) {

		if ( ! empty( $dto->email ) || ! empty( $dto->phone ) ) {
			$this->validate_customer_exists( $dto, $customer->id );
		}

		$this->fill_customer( $customer, $dto );

		$saved = $customer->save();

		if ( ! $saved ) {
			throw new Exception( esc_html__( 'Failed to update customer.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$this->save_relations( $customer, $dto, true );

		return $customer;
	}

	/**
	 * Delete a Customer.
	 *
	 * @since 1.0.0
	 *
	 * @param CustomerModel $customer The customer model to delete.
	 *
	 * @return bool
	 */
	public function delete( $customer ) {

		return $customer->delete();
	}

	/**
	 * Get an existing customer by email or phone.
	 *
	 * @since 1.0.0
	 *
	 * @param CustomerDTO $dto Data for customer creation.
	 *
	 * @return CustomerModel|null
	 *
	 * @throws Exception On email or phone not provided.
	 */
	public function get_existing_customer( $dto ) {

		if ( empty( $dto->email ) && empty( $dto->phone ) ) {
			throw new Exception( esc_html__( 'Customer email or phone must be provided.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$exists_query = CustomerModel::query()->select( [ 'id' ] );

		if ( ! empty( $dto->email ) && ! empty( $dto->phone ) ) {
			$exists_query->where( 'email', '=', $dto->email, '%s' )
				->or_where( 'phone', '=', $dto->phone, '%s' );
		} elseif ( ! empty( $dto->email ) ) {
			$exists_query->where( 'email', '=', $dto->email, '%s' );
		} elseif ( ! empty( $dto->phone ) ) {
			$exists_query->where( 'phone', '=', $dto->phone, '%s' );
		}

		return $exists_query->first();
	}

	/**
	 * Validate if a customer exists by email or phone.
	 *
	 * @since 1.0.0
	 *
	 * @param CustomerDTO $dto        Data for customer creation.
	 * @param int|null    $exclude_id Customer ID to exclude from the search.
	 *
	 * @return void
	 *
	 * @throws Exception On email or phone conflict.
	 */
	private function validate_customer_exists( $dto, $exclude_id = null ) {

		$base_query = CustomerModel::query()->select( [ 'id' ] );

		if ( $exclude_id ) {
			$base_query->where( 'id', '!=', $exclude_id, '%d' );
		}

		if ( ! empty( $dto->email ) ) {
			$email_query = clone $base_query;

			$email_query->where( 'email', '=', $dto->email, '%s' );

			if ( $email_query->first() ) {
				throw new Exception( esc_html__( 'Customer with this email already exists.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
			}
		}

		if ( ! empty( $dto->phone ) ) {
			$phone_query = clone $base_query;

			$phone_query->where( 'phone', '=', $dto->phone, '%s' );

			if ( $phone_query->first() ) {
				throw new Exception( esc_html__( 'Customer with this phone already exists.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
			}
		}
	}

	/**
	 * Fill a Customer model from DTO.
	 *
	 * @since 1.0.0
	 *
	 * @param CustomerModel $customer The customer model to fill.
	 * @param CustomerDTO   $dto      Data transfer object.
	 */
	private function fill_customer( $customer, $dto ) {

		if ( isset( $dto->name ) ) {
			$customer->name = $dto->name;
		}

		if ( ! empty( $dto->email ) ) {
			$customer->email = $dto->email;
		}

		if ( ! empty( $dto->phone ) ) {
			$customer->phone = $dto->phone;
		}

		if ( isset( $dto->notes ) ) {
			$customer->notes = $dto->notes;
		}
	}

	/**
	 * Save related data (tags) for a Customer.
	 *
	 * @since 1.0.0
	 *
	 * @param CustomerModel $customer  The customer model.
	 * @param CustomerDTO   $dto       Data transfer object.
	 * @param bool          $is_update Whether this is an update operation.
	 */
	private function save_relations( $customer, $dto, $is_update = false ) {

		// Attach tags.
		if ( isset( $dto->tags ) ) {
			if ( $is_update ) {
				$customer->tags()->sync( $dto->tags );
			} elseif ( ! empty( $dto->tags ) ) {
				$customer->tags()->attach( $dto->tags );
			}
		}
	}
}
