<?php

declare(strict_types=1);

namespace SecurWP\Modules\TwoFactor\Migrations;

use SecurWP\Core\Database\Migration;
use SecurWP\Core\Database\SchemaInspector;
use SecurWP\Core\Installer;
use SecurWP\Modules\TwoFactor\EmailOTPProvider;
use SecurWP\Modules\TwoFactor\LoginSessionStore;
use SecurWP\Modules\TwoFactor\RecoveryCodes;
use SecurWP\Modules\TwoFactor\TwoFactor;

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

final class TwoFactorSchemaV6 implements Migration {

	public function module_key(): string {
		return 'two_factor';
	}

	public function target_version(): int {
		return 6;
	}

	public function is_applied(): bool {
		global $wpdb;

		$table = $wpdb->prefix . 'securwp_two_factor';

		return TwoFactor::is_schema_current()
			&& RecoveryCodes::is_schema_current()
			&& EmailOTPProvider::is_setup_hash_schema_current()
			&& LoginSessionStore::is_hash_schema_current()
			&& null === SchemaInspector::get_column( $table, 'recovery_codes' )
			&& null === SchemaInspector::get_column( $table, 'recovery_storage_version' )
			&& null === SchemaInspector::get_column( $table, 'recovery_codes_migrated_at' );
	}

	public function apply(): bool {
		Installer::ensure_two_factor_table_schema();
		Installer::ensure_two_factor_recovery_codes_table_schema();
		Installer::ensure_two_factor_sessions_table_schema();
		$this->clear_pending_challenge_state();
		$this->drop_obsolete_recovery_columns();

		return $this->is_applied();
	}

	private function clear_pending_challenge_state(): void {
		global $wpdb;

			$two_factor_table_name = $wpdb->prefix . 'securwp_two_factor';
			if ( SchemaInspector::table_exists( $two_factor_table_name ) ) {
				$table_identifier = SchemaInspector::table_identifier( $two_factor_table_name );
				// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				$wpdb->query(
					"UPDATE {$table_identifier}
					 SET setup_email_code_hash = NULL,
					     setup_email_attempts = 0,
					     setup_email_sent_at = NULL,
					     setup_email_delivered = 0"
				);
				// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			}

		$sessions_table_name = $wpdb->prefix . 'securwp_two_factor_sessions';
		if ( SchemaInspector::table_exists( $sessions_table_name ) ) {
			$sessions_table = SchemaInspector::table_identifier( $sessions_table_name );
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			$wpdb->query( "DELETE FROM {$sessions_table}" );
		}
	}

	private function drop_obsolete_recovery_columns(): void {
		global $wpdb;

		$table_name = $wpdb->prefix . 'securwp_two_factor';
		if ( ! SchemaInspector::table_exists( $table_name ) ) {
			return;
		}

		foreach ( [ 'recovery_codes', 'recovery_storage_version', 'recovery_codes_migrated_at' ] as $column ) {
			if ( null === SchemaInspector::get_column( $table_name, $column ) ) {
				continue;
			}

			$table_identifier  = SchemaInspector::table_identifier( $table_name );
			if ( $column === '' || strlen( $column ) > 64 || ! preg_match( '/^[A-Za-z0-9_]+$/', $column ) ) {
				throw new \InvalidArgumentException( 'Invalid column identifier.' );
			}
			$column_identifier = '`' . str_replace( '`', '``', $column ) . '`';

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.SchemaChange
			$wpdb->query( "ALTER TABLE {$table_identifier} DROP COLUMN {$column_identifier}" );
		}
	}
}
