<?php

namespace SugarCalendarBookings;

use SugarCalendarBookings\Appointments\AppointmentModel;
use SugarCalendarBookings\Customers\CustomerModel;
use SugarCalendarBookings\Employees\EmployeeModel;
use SugarCalendarBookings\Locations\LocationModel;
use SugarCalendarBookings\Payments\PaymentModel;
use SugarCalendarBookings\Services\ServiceModel;
use SugarCalendarBookings\Support\Database\QueryBuilder;

/**
 * Delete manager.
 *
 * @since 1.0.0
 */
class DeleteManager {

	/**
	 * Check if provided type is supported by the delete manager.
	 *
	 * @since 1.0.0
	 *
	 * @param string $type Object type.
	 *
	 * @return bool
	 */
	public function is_supported_type( $type ) {

		return in_array(
			$type,
			[
				ServiceModel::get_type(),
				AppointmentModel::get_type(),
				LocationModel::get_type(),
				CustomerModel::get_type(),
				EmployeeModel::get_type(),
				PaymentModel::get_type(),
			],
			true
		);
	}

	/**
	 * Delete items.
	 *
	 * @since 1.0.0
	 *
	 * @param array  $ids  Object IDs.
	 * @param string $type Object type.
	 */
	public function delete( $ids, $type ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		switch ( $type ) {
			case ServiceModel::get_type():
				$this->delete_services( $ids );
				break;

			case AppointmentModel::get_type():
				$this->delete_appointments( $ids );
				break;

			case LocationModel::get_type():
				$this->delete_locations( $ids );
				break;

			case CustomerModel::get_type():
				$this->delete_customers( $ids );
				break;

			case EmployeeModel::get_type():
				$this->delete_employees( $ids );
				break;

			case PaymentModel::get_type():
				$this->delete_payments( $ids );
				break;
		}
	}

	/**
	 * Delete a service and all related data.
	 *
	 * @since 1.0.0
	 *
	 * @param array $service_ids Service IDs.
	 */
	public function delete_services( $service_ids ) {

		// Make sure we have valid service IDs.
		$service_ids = $this->query()->table( 'services' )
			->where( 'id', 'IN', $service_ids, '%d' )
			->column( 'id' );

		if ( empty( $service_ids ) ) {
			return;
		}

		// Delete services.
		$this->query()->table( 'services' )
			->where( 'id', 'IN', $service_ids, '%d' )
			->delete();

		// Delete service meta.
		$this->query()->table( 'meta' )
			->where( 'object_type', '=', ServiceModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $service_ids, '%d' )
			->delete();

		// Delete location service relationships.
		$this->query()->table( 'location_service' )
			->where( 'service_id', 'IN', $service_ids, '%d' )
			->delete();

		// Delete employee service relationships.
		$this->query()->table( 'employee_service' )
			->where( 'service_id', 'IN', $service_ids, '%d' )
			->delete();

		// Delete tag relationships.
		$this->query()->table( 'tag_relationships' )
			->where( 'object_type', '=', ServiceModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $service_ids, '%d' )
			->delete();

		// Delete related appointments.
		$related_appointments = $this->query()->table( 'appointments' )
			->where( 'service_id', 'IN', $service_ids, '%d' )
			->column( 'id' );

		if ( ! empty( $related_appointments ) ) {
			$this->delete_appointments( $related_appointments );
		}
	}

	/**
	 * Delete appointments.
	 *
	 * @since 1.0.0
	 *
	 * @param array $appointment_ids Appointment IDs.
	 */
	public function delete_appointments( $appointment_ids ) {

		// Make sure we have valid appointment IDs.
		$appointment_ids = $this->query()->table( 'appointments' )
			->where( 'id', 'IN', $appointment_ids, '%d' )
			->column( 'id' );

		if ( empty( $appointment_ids ) ) {
			return;
		}

		// Hydrate each appointment with its meta BEFORE the rows are wiped.
		// Listeners on `sugar_calendar_bookings_appointment_deleted` need to
		// read state off the model (e.g. external_calendar_events meta for
		// the Outlook hard-delete or meeting_id for Zoom) and would see
		// nothing if we fired the hook after the DELETE statements.
		// Batch-load with eager `meta` relation so bulk deletes don't fire
		// N+1 queries (one per appointment + one per meta load).
		$appointments = AppointmentModel::query()
			->with( [ 'meta' ] )
			->where( 'id', 'IN', $appointment_ids, '%d' )
			->get();

		foreach ( $appointments as $appointment ) {
			$appointment->load_meta();
		}

		// Delete appointments.
		$this->query()->table( 'appointments' )
			->where( 'id', 'IN', $appointment_ids, '%d' )
			->delete();

		// Delete appointment meta.
		$this->query()->table( 'meta' )
			->where( 'object_type', '=', AppointmentModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $appointment_ids, '%d' )
			->delete();

		// Delete appointment activity logs.
		$this->query()->table( 'activities' )
			->where( 'object_type', '=', AppointmentModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $appointment_ids, '%d' )
			->where( 'type', 'IN', [ 'note', 'log' ], '%s' )
			->delete();

		// Delete appointment payment relationships.
		$this->query()->table( 'appointment_payment' )
			->where( 'appointment_id', 'IN', $appointment_ids, '%d' )
			->delete();

		foreach ( $appointments as $appointment ) {
			/**
			 * Fires after an appointment is deleted from the database.
			 *
			 * Fired from the canonical delete path so every entry point —
			 * direct AppointmentService::delete(), admin bulk delete, and
			 * cascade deletes from related domain wipes (service, employee)
			 * — reaches the same listeners. The passed model still has its
			 * meta hydrated in-memory; the underlying DB rows are gone.
			 *
			 * @since 1.5.0
			 *
			 * @param AppointmentModel $appointment The deleted appointment.
			 */
			do_action( 'sugar_calendar_bookings_appointment_deleted', $appointment ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
		}
	}

	/**
	 * Delete locations.
	 *
	 * @since 1.0.0
	 *
	 * @param array $location_ids Location IDs.
	 */
	public function delete_locations( $location_ids ) {

		// Make sure we have valid location IDs.
		$location_ids = $this->query()->table( 'locations' )
			->where( 'id', 'IN', $location_ids, '%d' )
			->column( 'id' );

		if ( empty( $location_ids ) ) {
			return;
		}

		// Delete locations.
		$this->query()->table( 'locations' )
			->where( 'id', 'IN', $location_ids, '%d' )
			->delete();

		// Delete location meta.
		$this->query()->table( 'meta' )
			->where( 'object_type', '=', LocationModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $location_ids, '%d' )
			->delete();

		// Delete location service relationships.
		$this->query()->table( 'location_service' )
			->where( 'location_id', 'IN', $location_ids, '%d' )
			->delete();
	}

	/**
	 * Delete customers.
	 *
	 * @since 1.0.0
	 *
	 * @param array $customer_ids Customer IDs.
	 */
	public function delete_customers( $customer_ids ) {

		// Make sure we have valid customer IDs.
		$customer_ids = $this->query()->table( 'customers' )
			->where( 'id', 'IN', $customer_ids, '%d' )
			->column( 'id' );

		if ( empty( $customer_ids ) ) {
			return;
		}

		// Delete customers.
		$this->query()->table( 'customers' )
			->where( 'id', 'IN', $customer_ids, '%d' )
			->delete();

		// Delete customer meta.
		$this->query()->table( 'meta' )
			->where( 'object_type', '=', CustomerModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $customer_ids, '%d' )
			->delete();

		// Delete customer tags.
		$this->query()->table( 'tag_relationships' )
			->where( 'object_type', '=', CustomerModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $customer_ids, '%d' )
			->delete();
	}

	/**
	 * Delete employees.
	 *
	 * @since 1.0.0
	 * @since 1.2.0 Add appointment and tag relationships deletion.
	 *
	 * @param array $employee_ids Employee IDs.
	 */
	public function delete_employees( $employee_ids ) {

		// Make sure we have valid employee IDs.
		$employee_ids = $this->query()->table( 'employees' )
			->where( 'id', 'IN', $employee_ids, '%d' )
			->column( 'id' );

		if ( empty( $employee_ids ) ) {
			return;
		}

		// Delete employees.
		$this->query()->table( 'employees' )
			->where( 'id', 'IN', $employee_ids, '%d' )
			->delete();

		// Delete employee meta.
		$this->query()->table( 'meta' )
			->where( 'object_type', '=', EmployeeModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $employee_ids, '%d' )
			->delete();

		// Delete employee service relationships.
		$this->query()->table( 'employee_service' )
			->where( 'employee_id', 'IN', $employee_ids, '%d' )
			->delete();

		// Delete tag relationships.
		$this->query()->table( 'tag_relationships' )
			->where( 'object_type', '=', EmployeeModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $employee_ids, '%d' )
			->delete();

		// Delete related appointments.
		$related_appointments = $this->query()->table( 'appointments' )
			->where( 'employee_id', 'IN', $employee_ids, '%d' )
			->column( 'id' );

		if ( ! empty( $related_appointments ) ) {
			$this->delete_appointments( $related_appointments );
		}
	}

	/**
	 * Delete payments.
	 *
	 * @since 1.0.0
	 *
	 * @param array $payment_ids Payment IDs.
	 */
	public function delete_payments( $payment_ids ) {

		// Make sure we have valid payment IDs.
		$payment_ids = $this->query()->table( 'payments' )
			->where( 'id', 'IN', $payment_ids, '%d' )
			->column( 'id' );

		if ( empty( $payment_ids ) ) {
			return;
		}

		// Delete payments.
		$this->query()->table( 'payments' )
			->where( 'id', 'IN', $payment_ids, '%d' )
			->delete();

		// Delete payment meta.
		$this->query()->table( 'meta' )
			->where( 'object_type', '=', PaymentModel::get_type(), '%s' )
			->where( 'object_id', 'IN', $payment_ids, '%d' )
			->delete();

		// Delete payment items.
		$this->query()->table( 'payment_items' )
			->where( 'payment_id', 'IN', $payment_ids, '%d' )
			->delete();

		// TODO: Log payment deletion so it can be displayed in appointment detail page logs.
	}

	/**
	 * Get the query builder.
	 *
	 * @since 1.0.0
	 *
	 * @return QueryBuilder
	 */
	private function query() {

		return new QueryBuilder();
	}
}
