<?php

declare( strict_types=1 );

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

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

/**
 * Handles all CRUD operations for the playard_facilities table.
 */
class Playard_Facility {

	/**
	 * Creates or updates the facility during onboarding.
	 * INSERT on first run, UPDATE on re-run.
	 *
	 * @param array<string,mixed> $data
	 * @return int|false  Facility ID on success, false on failure.
	 */
	public function save_onboarding( array $data ): int|false {
		$existing_id = playard_get_facility_id();

		if ( $existing_id > 0 ) {
			return $this->update( $existing_id, $data );
		}

		return $this->create( $data );
	}

	/**
	 * @param array<string,mixed> $data
	 * @return int|false
	 */
	public function create( array $data ): int|false {
		global $wpdb;

		$result = $wpdb->insert(
			$wpdb->prefix . 'playard_facilities',
			[
				'name'            => sanitize_text_field( $data['name'] ),
				'address'         => sanitize_text_field( $data['address'] ?? '' ),
				'phone'           => sanitize_text_field( $data['phone'] ?? '' ),
				'email'           => sanitize_email( $data['email'] ?? '' ),
				'open_time'       => sanitize_text_field( $data['open_time'] ?? '06:00:00' ),
				'close_time'      => sanitize_text_field( $data['close_time'] ?? '22:00:00' ),
				'open_days'       => wp_json_encode( array_map( 'sanitize_text_field', (array) ( $data['open_days'] ?? [] ) ) ),
				'timezone'        => sanitize_text_field( $data['timezone'] ?? 'UTC' ),
				'currency'        => sanitize_text_field( $data['currency'] ?? 'USD' ),
				'logo_url'        => esc_url_raw( $data['logo_url'] ?? '' ),
				'cover_photo_url' => esc_url_raw( $data['cover_photo_url'] ?? '' ),
				'lat'             => (float) ( $data['lat'] ?? 0.0 ),
				'lng'             => (float) ( $data['lng'] ?? 0.0 ),
				'status'          => 'active',
			],
			[ '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%f', '%f', '%s' ]
		);

		if ( false === $result ) {
			return false;
		}

		$facility_id = (int) $wpdb->insert_id;
		update_option( 'playard_facility_id', $facility_id, 'no' );

		do_action( 'playard_facility_created', $facility_id, $data );

		return $facility_id;
	}

	/**
	 * @param int                 $id
	 * @param array<string,mixed> $data
	 * @return int|false  Returns the facility ID on success, false on failure.
	 */
	public function update( int $id, array $data ): int|false {
		global $wpdb;

		$result = $wpdb->update(
			$wpdb->prefix . 'playard_facilities',
			[
				'name'            => sanitize_text_field( $data['name'] ),
				'address'         => sanitize_text_field( $data['address'] ?? '' ),
				'phone'           => sanitize_text_field( $data['phone'] ?? '' ),
				'email'           => sanitize_email( $data['email'] ?? '' ),
				'open_time'       => sanitize_text_field( $data['open_time'] ?? '06:00:00' ),
				'close_time'      => sanitize_text_field( $data['close_time'] ?? '22:00:00' ),
				'open_days'       => wp_json_encode( array_map( 'sanitize_text_field', (array) ( $data['open_days'] ?? [] ) ) ),
				'timezone'        => sanitize_text_field( $data['timezone'] ?? 'UTC' ),
				'currency'        => sanitize_text_field( $data['currency'] ?? 'USD' ),
				'logo_url'        => esc_url_raw( $data['logo_url'] ?? '' ),
				'cover_photo_url' => esc_url_raw( $data['cover_photo_url'] ?? '' ),
				'lat'             => (float) ( $data['lat'] ?? 0.0 ),
				'lng'             => (float) ( $data['lng'] ?? 0.0 ),
			],
			[ 'id' => $id ],
			[ '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%f', '%f' ],
			[ '%d' ]
		);

		if ( false === $result ) {
			return false;
		}

		do_action( 'playard_facility_updated', $id, $data );

		return $id;
	}

	/**
	 * Updates only the fields provided — safe for partial updates from individual settings sections.
	 *
	 * @param int                 $id
	 * @param array<string,mixed> $data
	 */
	public function update_partial( int $id, array $data ): bool {
		global $wpdb;

		$format_map = [
			'name'            => '%s',
			'description'     => '%s',
			'address'         => '%s',
			'phone'           => '%s',
			'email'           => '%s',
			'logo_url'        => '%s',
			'cover_photo_url' => '%s',
			'open_time'       => '%s',
			'close_time'      => '%s',
			'open_days'       => '%s',
			'timezone'        => '%s',
			'currency'        => '%s',
			'status'          => '%s',
			'lat'             => '%f',
			'lng'             => '%f',
		];

		$update_data   = [];
		$update_format = [];

		foreach ( $data as $key => $value ) {
			if ( ! array_key_exists( $key, $format_map ) ) {
				continue;
			}

			// Validate timezone against PHP's known identifiers.
			if ( $key === 'timezone' ) {
				if ( ! in_array( $value, DateTimeZone::listIdentifiers(), true ) ) {
					continue;
				}
			}

			// Validate currency against the allowed list.
			if ( $key === 'currency' ) {
				$valid_currencies = array_keys( playard_get_currency_list() );
				if ( ! in_array( (string) $value, $valid_currencies, true ) ) {
					continue;
				}
			}

			// open_days must be validated against the whitelist and JSON-encoded.
			if ( $key === 'open_days' ) {
				$allowed_days        = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ];
				$days                = array_values( array_filter(
					array_map( 'sanitize_text_field', (array) $value ),
					fn( string $d ) => in_array( $d, $allowed_days, true )
				) );
				$update_data[ $key ] = wp_json_encode( $days );
				$update_format[]     = '%s';
				continue;
			}

			$update_data[ $key ] = $value;
			$update_format[]     = $format_map[ $key ];
		}

		if ( empty( $update_data ) ) {
			return false;
		}

		$result = $wpdb->update(
			$wpdb->prefix . 'playard_facilities',
			$update_data,
			[ 'id' => $id ],
			$update_format,
			[ '%d' ]
		);

		if ( false === $result ) {
			return false;
		}

		do_action( 'playard_facility_updated', $id, $data );

		return true;
	}

	// -------------------------------------------------------------------------
	// Holiday / Closure methods
	// -------------------------------------------------------------------------

	/**
	 * Adds a holiday date to the playard_holidays option.
	 * Returns the new holiday's auto-incremented ID.
	 *
	 * @param string $date    YYYY-MM-DD
	 * @param string $reason  Optional human-readable label
	 */
	public function add_holiday( string $date, string $reason ): int {
		$holidays = $this->get_holidays();
		$max_id   = empty( $holidays ) ? 0 : (int) max( array_column( $holidays, 'id' ) );
		$id       = $max_id + 1;

		$holidays[] = [
			'id'     => $id,
			'date'   => sanitize_text_field( $date ),
			'reason' => sanitize_text_field( $reason ),
		];

		update_option( 'playard_holidays', wp_json_encode( $holidays ), 'no' );

		do_action( 'playard_holiday_added', $date );

		return $id;
	}

	/**
	 * Removes a holiday by ID.
	 * Returns false when the ID is not found.
	 */
	public function remove_holiday( int $id ): bool {
		$holidays = $this->get_holidays();
		$filtered = array_values( array_filter(
			$holidays,
			fn( array $h ) => (int) $h['id'] !== $id
		) );

		if ( count( $filtered ) === count( $holidays ) ) {
			return false;
		}

		update_option( 'playard_holidays', wp_json_encode( $filtered ), 'no' );

		do_action( 'playard_holiday_removed', $id );

		return true;
	}

	/**
	 * Returns true when the given date is a blocked holiday.
	 *
	 * @param string $date  YYYY-MM-DD
	 */
	public function is_holiday( string $date ): bool {
		return playard_is_holiday( $date );
	}

	/**
	 * Returns all holidays as a plain array.
	 *
	 * @return array<int,array{id:int,date:string,reason:string}>
	 */
	public function get_holidays(): array {
		$raw = (string) get_option( 'playard_holidays', '[]' );
		$arr = json_decode( $raw, true );

		return is_array( $arr ) ? $arr : [];
	}

	/**
	 * @return object|null
	 */
	public function get( int $id ): ?object {
		global $wpdb;

		$row = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}playard_facilities WHERE id = %d LIMIT 1",
				$id
			)
		);

		return $row ?: null;
	}
}
