<?php

namespace SugarCalendarBookings\Abilities\Appointments;

use SugarCalendarBookings\Abilities\AbstractAbility;
use SugarCalendarBookings\Appointments\AppointmentModel;
use WP_Error;

/**
 * Ability: list appointments with filters and pagination.
 *
 * @since 1.5.0
 */
class ListAppointmentsAbility extends AbstractAbility {

	/**
	 * Ability slug, without the namespace prefix.
	 *
	 * @since 1.5.0
	 *
	 * @return string
	 */
	public function get_name(): string {

		return 'list-appointments';
	}

	/**
	 * Human-readable label.
	 *
	 * @since 1.5.0
	 *
	 * @return string
	 */
	public function get_label(): string {

		return esc_html__( 'List Appointments', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Human-readable description.
	 *
	 * @since 1.5.0
	 *
	 * @return string
	 */
	public function get_description(): string {

		return esc_html__( 'List bookings with filters and pagination.', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Input schema.
	 *
	 * @since 1.5.0
	 *
	 * @return array
	 */
	public function get_input_schema(): array {

		return [
			'type'       => 'object',
			'properties' => array_merge(
				self::SCHEMA_PAGINATION,
				self::SCHEMA_DATE_RANGE,
				[
					'status'      => [
						'type' => 'string',
						'enum' => [
							AppointmentModel::STATUS_DRAFT,
							AppointmentModel::STATUS_PENDING,
							AppointmentModel::STATUS_APPROVED,
							AppointmentModel::STATUS_CANCELLED,
							AppointmentModel::STATUS_DONE,
						],
					],
					'service_id'  => [
						'type'    => 'integer',
						'minimum' => 1,
					],
					'customer_id' => [
						'type'    => 'integer',
						'minimum' => 1,
					],
				]
			),
		];
	}

	/**
	 * Output schema.
	 *
	 * @since 1.5.0
	 *
	 * @return array
	 */
	public function get_output_schema(): array {

		return [
			'type'       => 'object',
			'properties' => [
				'appointments' => [ 'type' => 'array' ],
				'total'        => [ 'type' => 'integer' ],
			],
		];
	}

	/**
	 * Execute the query and return the formatted list.
	 *
	 * @since 1.5.0
	 *
	 * @param mixed $input Input data.
	 *
	 * @return array|WP_Error
	 */
	public function execute( $input ) {

		$args = $this->normalize_input( $input );

		$validation = $this->validate_date_range( $args );

		if ( is_wp_error( $validation ) ) {
			return $validation;
		}

		$pagination = $this->get_pagination( $args );
		$limit      = $pagination['limit'];
		$offset     = $pagination['offset'];

		$query = AppointmentModel::query()->with(
			[
				'service'  => static fn( $q ) => $q->select( [ 'id', 'name', 'color', 'duration', 'price' ] ),
				'customer' => static fn( $q ) => $q->select( [ 'id', 'name', 'email', 'phone' ] ),
				'employee' => static fn( $q ) => $q->select( [ 'id', 'name', 'email' ] ),
				'location' => static fn( $q ) => $q->select( [ 'id', 'name', 'address', 'city', 'country_code', 'timezone' ] ),
				'payments' => static fn( $q ) => $q->select( [ 'id', 'status', 'total_amount', 'currency', 'gateway' ] )->order_by( 'created_at', 'ASC' ),
				'meta'     => static fn( $q ) => $q->where( 'meta_key', 'IN', [ 'online_location', 'customer_timezone', 'internal_notes', 'customer_notes', 'ignore_availability' ], '%s' ),
			]
		);

		$count_query = AppointmentModel::query();

		$this->apply_filters_to_query( $query, $args );
		$this->apply_filters_to_query( $count_query, $args );

		$total = (int) $count_query->count();

		$query->order_by( 'appointments.start_at', 'DESC' )
			->offset( $offset )
			->limit( $limit );

		$rows      = $query->get();
		$formatter = $this->formatter();

		$out = [];

		foreach ( $rows as $row ) {
			$out[] = $formatter->appointment( $row );
		}

		return [
			'appointments' => $out,
			'total'        => $total,
		];
	}

	/**
	 * Apply input filters to a query builder.
	 *
	 * @since 1.5.0
	 *
	 * @param mixed $query A ModelQueryBuilder for AppointmentModel.
	 * @param array $args  Normalized input.
	 */
	private function apply_filters_to_query( $query, array $args ): void {

		if ( ! empty( $args['status'] ) ) {
			$query->where( 'appointments.status', '=', (string) $args['status'], '%s' );
		}

		if ( ! empty( $args['service_id'] ) ) {
			$query->where( 'appointments.service_id', '=', (int) $args['service_id'], '%d' );
		}

		if ( ! empty( $args['customer_id'] ) ) {
			$query->where( 'appointments.customer_id', '=', (int) $args['customer_id'], '%d' );
		}

		if ( ! empty( $args['date_from'] ) ) {
			$from_utc = $this->local_day_boundary_to_utc( (string) $args['date_from'], '00:00:00' );

			if ( $from_utc !== null ) {
				$query->where( 'appointments.start_at', '>=', $from_utc, '%s' );
			}
		}

		if ( ! empty( $args['date_to'] ) ) {
			$to_utc = $this->local_day_boundary_to_utc( (string) $args['date_to'], '23:59:59' );

			if ( $to_utc !== null ) {
				$query->where( 'appointments.start_at', '<=', $to_utc, '%s' );
			}
		}
	}
}
