<?php
declare(strict_types=1);
namespace ConfManager\Events;

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


use ConfManager\Traits\Singleton;
use ConfManager\Security\AuditLog;

/**
 * セッション管理
 *
 * イベント配下のセッション（ワークショップ・懇親会等）のCRUDを行う。
 * 追加料金やソート順も管理する。
 *
 * @since 1.0.0
 */
class SessionManager {

	use Singleton;

	private const TABLE = 'conf_sessions';

	/**
	 * 有効なセッション種別
	 */
	private const VALID_TYPES = [ 'main', 'workshop', 'social', 'other' ];

	/**
	 * 初期化
	 */
	protected function init(): void {}

	/**
	 * テーブル名を取得
	 */
	private function table(): string {
		global $wpdb;
		return $wpdb->prefix . self::TABLE;
	}

	/**
	 * セッションを作成する
	 *
	 * @param array $data {
	 *     @type int    $event_id     イベントID（必須）
	 *     @type string $title        タイトル（必須）
	 *     @type string $session_type セッション種別（main|workshop|social|other）
	 *     @type string $date_start   開始日時（必須）
	 *     @type string $date_end     終了日時（必須）
	 *     @type int    $capacity     定員
	 *     @type int    $fee          追加料金（0=メイン参加費に含む）
	 *     @type int    $sort_order   表示順
	 * }
	 * @return int|\WP_Error
	 */
	public function create( array $data ): int|\WP_Error {
		global $wpdb;

		$error = $this->validate( $data );
		if ( is_wp_error( $error ) ) {
			return $error;
		}

		// イベントの存在確認
		$event = EventManager::instance()->get( absint( $data['event_id'] ) );
		if ( null === $event ) {
			return new \WP_Error( 'event_not_found', __( 'The specified event was not found.', 'conferencemanager' ) );
		}

		$insert_data = [
			'event_id'     => absint( $data['event_id'] ),
			'title'        => sanitize_text_field( $data['title'] ),
			'session_type' => sanitize_text_field( $data['session_type'] ?? 'main' ),
			'date_start'   => sanitize_text_field( $data['date_start'] ),
			'date_end'     => sanitize_text_field( $data['date_end'] ),
			'fee'          => absint( $data['fee'] ?? 0 ),
			'sort_order'   => (int) ( $data['sort_order'] ?? 0 ),
		];

		if ( ! empty( $data['capacity'] ) ) {
			$insert_data['capacity'] = absint( $data['capacity'] );
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
		$result = $wpdb->insert( $this->table(), $insert_data );

		if ( false === $result ) {
			return new \WP_Error(
				'db_insert_error',
				__( 'Failed to create session.', 'conferencemanager' ),
				$wpdb->last_error
			);
		}

		$session_id = (int) $wpdb->insert_id;

		AuditLog::instance()->log( 'create_session', 'session', $session_id, [
			'event_id' => $data['event_id'],
			'title'    => $data['title'],
		] );

		return $session_id;
	}

	/**
	 * セッションを取得する
	 *
	 * @param int $id セッションID
	 * @return array|null
	 */
	public function get( int $id ): ?array {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$row = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$this->table()} WHERE id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				$id
			),
			ARRAY_A
		);

		return $row ?: null;
	}

	/**
	 * イベントに紐づくセッション一覧を取得する
	 *
	 * @param int $event_id イベントID
	 * @return array
	 */
	public function get_by_event( int $event_id ): array {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM {$this->table()} WHERE event_id = %d ORDER BY sort_order ASC, date_start ASC", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				$event_id
			),
			ARRAY_A
		);

		return $rows ?: [];
	}

	/**
	 * セッションを更新する
	 *
	 * @param int   $id   セッションID
	 * @param array $data 更新データ
	 * @return true|\WP_Error
	 */
	public function update( int $id, array $data ): true|\WP_Error {
		global $wpdb;

		$existing = $this->get( $id );
		if ( null === $existing ) {
			return new \WP_Error( 'not_found', __( 'Session not found.', 'conferencemanager' ) );
		}

		// セッション種別のバリデーション
		if ( isset( $data['session_type'] ) && ! in_array( $data['session_type'], self::VALID_TYPES, true ) ) {
			return new \WP_Error( 'invalid_session_type', __( 'Invalid session type.', 'conferencemanager' ) );
		}

		// 日付バリデーション
		$start = $data['date_start'] ?? $existing['date_start'];
		$end   = $data['date_end'] ?? $existing['date_end'];
		if ( $start && $end && strtotime( $start ) >= strtotime( $end ) ) {
			return new \WP_Error( 'invalid_dates', __( 'End date must be after start date.', 'conferencemanager' ) );
		}

		$update_data = [];

		$text_fields = [ 'title', 'session_type' ];
		foreach ( $text_fields as $field ) {
			if ( isset( $data[ $field ] ) ) {
				$update_data[ $field ] = sanitize_text_field( $data[ $field ] );
			}
		}

		$datetime_fields = [ 'date_start', 'date_end' ];
		foreach ( $datetime_fields as $field ) {
			if ( isset( $data[ $field ] ) ) {
				$update_data[ $field ] = sanitize_text_field( $data[ $field ] );
			}
		}

		if ( isset( $data['capacity'] ) ) {
			$update_data['capacity'] = absint( $data['capacity'] ) ?: null;
		}

		if ( isset( $data['fee'] ) ) {
			$update_data['fee'] = absint( $data['fee'] );
		}

		if ( isset( $data['sort_order'] ) ) {
			$update_data['sort_order'] = (int) $data['sort_order'];
		}

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

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table; request-local write, no caching needed.
		$result = $wpdb->update( $this->table(), $update_data, [ 'id' => $id ] );

		if ( false === $result ) {
			return new \WP_Error(
				'db_update_error',
				__( 'Failed to update session.', 'conferencemanager' ),
				$wpdb->last_error
			);
		}

		AuditLog::instance()->log( 'update_session', 'session', $id, [
			'fields' => array_keys( $update_data ),
		] );

		return true;
	}

	/**
	 * セッションを削除する（物理削除）
	 *
	 * @param int $id セッションID
	 * @return true|\WP_Error
	 */
	public function delete( int $id ): true|\WP_Error {
		global $wpdb;

		$existing = $this->get( $id );
		if ( null === $existing ) {
			return new \WP_Error( 'not_found', __( 'Session not found.', 'conferencemanager' ) );
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table; request-local write, no caching needed.
		$result = $wpdb->delete( $this->table(), [ 'id' => $id ] );

		if ( false === $result ) {
			return new \WP_Error(
				'db_delete_error',
				__( 'Failed to delete session.', 'conferencemanager' ),
				$wpdb->last_error
			);
		}

		AuditLog::instance()->log( 'delete_session', 'session', $id, [
			'event_id' => $existing['event_id'],
			'title'    => $existing['title'],
		] );

		return true;
	}

	/**
	 * イベントの全セッションを削除する
	 *
	 * @param int $event_id イベントID
	 * @return int 削除件数
	 */
	public function delete_by_event( int $event_id ): int {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table; request-local write, no caching needed.
		return (int) $wpdb->delete( $this->table(), [ 'event_id' => $event_id ] );
	}

	/**
	 * バリデーション
	 */
	private function validate( array $data ): true|\WP_Error {
		if ( empty( $data['event_id'] ) ) {
			return new \WP_Error( 'missing_event_id', __( 'Event ID is required.', 'conferencemanager' ) );
		}

		if ( empty( $data['title'] ) ) {
			return new \WP_Error( 'missing_title', __( 'Title is required.', 'conferencemanager' ) );
		}

		if ( empty( $data['date_start'] ) || empty( $data['date_end'] ) ) {
			return new \WP_Error( 'missing_dates', __( 'Start date and end date are required.', 'conferencemanager' ) );
		}

		if ( strtotime( $data['date_start'] ) >= strtotime( $data['date_end'] ) ) {
			return new \WP_Error( 'invalid_dates', __( 'End date must be after start date.', 'conferencemanager' ) );
		}

		if ( isset( $data['session_type'] ) && ! in_array( $data['session_type'], self::VALID_TYPES, true ) ) {
			return new \WP_Error( 'invalid_session_type', __( 'Invalid session type.', 'conferencemanager' ) );
		}

		return true;
	}

	/**
	 * 有効なセッション種別一覧
	 */
	public static function get_valid_types(): array {
		return self::VALID_TYPES;
	}
}
