<?php
/**
 * BitForgeNet Guest Tickets - Organizer model.
 *
 * @package BitForgeNet_Guest_Tickets
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

// This file talks to the plugin's own tables. WordPress has no API for them --
// they are not posts and not meta -- so $wpdb is the only way in. The rows
// change on every booking and every scan, which makes an object cache wrong
// more often than it is right. Table names are built from $wpdb->prefix and
// never from a request, so interpolating one carries nothing to inject; every
// value goes through $wpdb->prepare(), and the few identifiers that cannot
// (ORDER BY columns) are matched against a whitelist first.
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter


/**
 * Data access for organizer profiles.
 */
class BFNET_Organizer {

	/**
	 * Table name.
	 *
	 * @return string
	 */
	public static function table() {
		return BFNET_Install::table( 'organizers' );
	}

	/**
	 * Fetch one organizer.
	 *
	 * @param int $id Organizer id.
	 * @return object|null
	 */
	public static function get( $id ) {
		global $wpdb;
		$table = self::table();
		return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", (int) $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL
	}

	/**
	 * Fetch all organizers.
	 *
	 * @param string $status Optional status filter.
	 * @return array
	 */
	public static function all( $status = 'active' ) {
		global $wpdb;
		$table = self::table();
		if ( $status ) {
			return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE status = %s ORDER BY name ASC", $status ) ); // phpcs:ignore WordPress.DB.PreparedSQL
		}
		return $wpdb->get_results( "SELECT * FROM {$table} ORDER BY name ASC" ); // phpcs:ignore WordPress.DB.PreparedSQL
	}

	/**
	 * The first active organizer, used as a default.
	 *
	 * @return object|null
	 */
	public static function primary() {
		$all = self::all( 'active' );
		return $all ? $all[0] : null;
	}

	/**
	 * Insert or update an organizer.
	 *
	 * @param array $data Field values.
	 * @return int|WP_Error
	 */
	public static function save( array $data ) {
		global $wpdb;

		$id   = isset( $data['id'] ) ? (int) $data['id'] : 0;
		$name = isset( $data['name'] ) ? sanitize_text_field( $data['name'] ) : '';

		if ( '' === trim( $name ) ) {
			return new WP_Error( 'bfnet_organizer_name', __( 'The organizer needs a name.', 'bitforgenet-guest-tickets' ) );
		}

		$fields = array(
			'name'            => $name,
			'slug'            => sanitize_title( ! empty( $data['slug'] ) ? $data['slug'] : $name ),
			'tagline'         => isset( $data['tagline'] ) ? sanitize_text_field( $data['tagline'] ) : '',
			'description'     => isset( $data['description'] ) ? wp_kses_post( $data['description'] ) : '',
			'logo_id'         => isset( $data['logo_id'] ) ? (int) $data['logo_id'] : 0,
			'email'           => isset( $data['email'] ) ? sanitize_email( $data['email'] ) : '',
			'phone'           => isset( $data['phone'] ) ? BFNET_Security::clean_phone( $data['phone'] ) : '',
			'whatsapp'        => isset( $data['whatsapp'] ) ? BFNET_Security::clean_phone( $data['whatsapp'] ) : '',
			'website'         => isset( $data['website'] ) ? esc_url_raw( $data['website'] ) : '',
			'instagram'       => isset( $data['instagram'] ) ? esc_url_raw( $data['instagram'] ) : '',
			'facebook'        => isset( $data['facebook'] ) ? esc_url_raw( $data['facebook'] ) : '',
			'brand_primary'   => isset( $data['brand_primary'] ) ? ( sanitize_hex_color( $data['brand_primary'] ) ?: '#C9A227' ) : '#C9A227',
			'brand_secondary' => isset( $data['brand_secondary'] ) ? ( sanitize_hex_color( $data['brand_secondary'] ) ?: '#0B0B0B' ) : '#0B0B0B',
			'status'          => ( isset( $data['status'] ) && 'inactive' === $data['status'] ) ? 'inactive' : 'active',
			'updated_at'      => current_time( 'mysql' ),
		);

		if ( $id ) {
			$wpdb->update( self::table(), $fields, array( 'id' => $id ), null, array( '%d' ) );
			BFNET_Log::audit( 'organizer', $id, 'organizer_updated', array( 'name' => $name ) );
		} else {
			$fields['created_at'] = current_time( 'mysql' );
			$wpdb->insert( self::table(), $fields );
			$id = (int) $wpdb->insert_id;
			BFNET_Log::audit( 'organizer', $id, 'organizer_created', array( 'name' => $name ) );
		}

		return $id ? $id : new WP_Error( 'bfnet_organizer_save', __( 'The organizer could not be saved.', 'bitforgenet-guest-tickets' ) );
	}

	/**
	 * Delete an organizer if no event references it.
	 *
	 * @param int $id Organizer id.
	 * @return true|WP_Error
	 */
	public static function delete( $id ) {
		global $wpdb;

		$events = BFNET_Install::table( 'events' );
		$used   = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$events} WHERE organizer_id = %d", (int) $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL

		if ( $used > 0 ) {
			return new WP_Error( 'bfnet_organizer_in_use', __( 'This organizer is attached to existing events. Set it to inactive instead of deleting it.', 'bitforgenet-guest-tickets' ) );
		}

		$wpdb->delete( self::table(), array( 'id' => (int) $id ), array( '%d' ) );
		BFNET_Log::audit( 'organizer', $id, 'organizer_deleted', array(), 'warning' );

		return true;
	}

	/**
	 * Logo URL with fallback to the bundled brand mark.
	 *
	 * @param object|null $organizer Organizer row.
	 * @return string
	 */
	public static function logo_url( $organizer ) {
		if ( $organizer && ! empty( $organizer->logo_id ) ) {
			$url = wp_get_attachment_image_url( (int) $organizer->logo_id, 'medium' );
			if ( $url ) {
				return $url;
			}
		}
		return BFNET_URL . 'assets/images/bitforgenet-guest-tickets-logo.png';
	}
}
