<?php

namespace SugarCalendarBookings\Integrations\OAuthRelay;

use DateTimeImmutable;
use SugarCalendarBookings\Integrations\IntegrationCapabilityRegistry;
use SugarCalendarBookings\Integrations\WebhookSubscriptionLifecycleInterface;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Support\Database\AbstractModel;

/**
 * OAuth Connection model.
 *
 * @since 1.4.0
 *
 * @property int                    $id
 * @property int                    $user_id
 * @property string                 $provider
 * @property string                 $status
 * @property string                 $access_token
 * @property string                 $refresh_token
 * @property DateTimeImmutable      $expires_at
 * @property string                 $token_type
 * @property string                 $scope
 * @property int                    $app_id
 * @property string                 $account_id
 * @property string                 $account_email
 * @property string                 $account_name
 * @property string                 $account_avatar
 * @property DateTimeImmutable      $connected_at
 * @property DateTimeImmutable|null $refreshed_at
 * @property array                  $settings
 * @property string|null            $subscription_id
 */
class OAuthConnectionModel extends AbstractModel {

	/**
	 * The database table name without prefix.
	 *
	 * @since 1.4.0
	 *
	 * @var string
	 */
	protected static $table_name = 'oauth_connections';

	/**
	 * The model type identifier.
	 *
	 * @since 1.4.0
	 *
	 * @var string
	 */
	protected static $type = 'oauth_connection';

	/**
	 * Column formats for the model.
	 *
	 * @since 1.4.0
	 *
	 * @var array<string, string>
	 */
	protected static $column_formats = [
		'id'              => '%d',
		'user_id'         => '%d',
		'provider'        => '%s',
		'status'          => '%s',
		'access_token'    => '%s',
		'refresh_token'   => '%s',
		'expires_at'      => '%s',
		'token_type'      => '%s',
		'scope'           => '%s',
		'app_id'          => '%d',
		'account_id'      => '%s',
		'account_email'   => '%s',
		'account_name'    => '%s',
		'account_avatar'  => '%s',
		'connected_at'    => '%s',
		'refreshed_at'    => '%s',
		'settings'        => '%s',
		'subscription_id' => '%s',
	];

	/**
	 * The attributes and meta that should be cast.
	 *
	 * @since 1.4.0
	 *
	 * @var array<string, string>
	 */
	protected static $casts = [
		'id'           => 'integer',
		'user_id'      => 'integer',
		'app_id'       => 'integer',
		'expires_at'   => 'datetime',
		'connected_at' => 'datetime',
		'refreshed_at' => 'datetime',
		'settings'     => 'array',
	];

	/**
	 * Column defaults for the model.
	 *
	 * @since 1.4.0
	 *
	 * @return array<string, mixed>
	 */
	protected static function column_defaults() {

		return [
			'status'     => 'active',
			'token_type' => 'bearer',
		];
	}

	/**
	 * Batch-fetch connection models by ID.
	 *
	 * Lets callers replace N `OAuthConnectionModel::get( $id )` calls inside a
	 * loop with a single `WHERE id IN (...)` query. Returned map is keyed by
	 * connection ID; missing IDs are simply absent from the map.
	 *
	 * @since 1.5.0
	 *
	 * @param int[] $ids Connection IDs to fetch.
	 *
	 * @return array<int, self> Map of id => model.
	 */
	public static function get_many( array $ids ): array {

		$ids = array_values( array_unique( array_map( 'intval', $ids ) ) );

		if ( empty( $ids ) ) {
			return [];
		}

		$rows = self::query()
			->where( 'id', 'IN', $ids, '%d' )
			->get();

		$map = [];

		foreach ( $rows as $row ) {
			$map[ (int) $row->id ] = $row;
		}

		return $map;
	}

	/**
	 * Delete the connection.
	 *
	 * Resolves the provider's {@see WebhookSubscriptionLifecycleInterface}
	 * capability via the registry and tears down the provider-side
	 * subscription before the row is removed. Providers that don't expose
	 * the capability (e.g. Zoom — its relay path handles teardown elsewhere)
	 * are silently skipped, so disconnect call-sites stay provider-agnostic.
	 *
	 * @since 1.5.0
	 *
	 * @param bool $clean_up Whether to clean up related data.
	 *
	 * @return bool True on success, false on failure.
	 */
	public function delete( $clean_up = true ) {

		if ( empty( $this->get_id() ) ) {
			return false;
		}

		$lifecycle = Plugin::get( IntegrationCapabilityRegistry::class )
			->find( WebhookSubscriptionLifecycleInterface::class, (string) $this->provider );

		if ( $lifecycle instanceof WebhookSubscriptionLifecycleInterface ) {
			$lifecycle->unregister( $this );
		}

		return parent::delete( $clean_up );
	}
}
