<?php
/**
 * Test PostImageField.
 *
 * @package Tests\WPGraphQL\GF
 */

namespace Tests\WPGraphQL\GF;

use GFFormsModel;
use Tests\WPGraphQL\GF\TestCase\FormFieldTestCase;
use Tests\WPGraphQL\GF\TestCase\FormFieldTestCaseInterface;
use WPGraphQL\GF\Utils\GFUtils;

class FooPostImage extends \GF_Field_Post_Image {
	public function upload_file( $form_id, $file ) {
		\GFCommon::log_debug( __METHOD__ . '(): Uploading file: ' . $file['name'] );
		$target = GFFormsModel::get_file_upload_path( $form_id, $file['name'] );
		if ( ! $target ) {
			\GFCommon::log_debug( __METHOD__ . '(): FAILED (Upload folder could not be created.)' );

			return 'FAILED (Upload folder could not be created.)';
		}
		\GFCommon::log_debug( __METHOD__ . '(): Upload folder is ' . print_r( $target, true ) );

		if ( copy( $file['tmp_name'], $target['path'] ) ) {
			\GFCommon::log_debug( __METHOD__ . '(): File ' . $file['tmp_name'] . ' successfully moved to ' . $target['path'] . '.' );
			$this->set_permissions( $target['path'] );

			return $target['url'];
		} else {
			\GFCommon::log_debug( __METHOD__ . '(): FAILED (Temporary file ' . $file['tmp_name'] . ' could not be copied to ' . $target['path'] . '.)' );

			return 'FAILED (Temporary file could not be copied.)';
		}
	}
}
/**
 * Class -PostImageFieldTest
 */
class PostImageFieldTest extends FormFieldTestCase implements FormFieldTestCaseInterface {
	// Since we cant detect if draft field is queried.
	/**
	 * Set up.
	 */
	public function setUp(): void {

		// Before...
		copy( __DIR__ . '/../_support/files/img1.png', '/tmp/img1.png' );
		$stat  = stat( dirname( '/tmp/img1.png' ) );
		$perms = $stat['mode'] & 0000666;
		chmod( '/tmp/img1.png', $perms );
		copy( __DIR__ . '/../_support/files/img2.png', '/tmp/img2.png' );
		$stat  = stat( dirname( '/tmp/img2.png' ) );
		$perms = $stat['mode'] & 0000666;
		chmod( '/tmp/img2.png', $perms );

		add_filter( 'gform_gf_field_create', [ $this, 'mock_post_image_field' ], 10, 2 );

		parent::setUp();

		global $_gf_uploaded_files;
		$_gf_uploaded_files = [];
		$_POST              = [];
	}

	/**
	 * Tear down
	 */
	public function tearDown(): void {
		GFFormsModel::delete_files( $this->entry_id, $this->factory->form->get_object_by_id( $this->form_id ) );
		remove_filter( 'gform_gf_field_create', [ $this, 'mock_post_image_field' ], 10 );

		parent::tearDown();
	}

	/**
	 * Tests the field properties and values.
	 */
	public function testField(): void {
		$this->runTestField();
	}

	/**
	 * Tests submitting the field values as a draft entry with submitGfForm.
	 */
	public function testSubmitDraft(): void {
		$this->runTestSubmitDraft();
	}

	/**
	 * Tests submitting the field values as an entry with submitGfForm.
	 */
	public function testSubmitForm(): void {
		$this->runtestSubmitForm();
	}

	/**
	 * Tests updating the field value with updateGfEntry.
	 */
	public function testUpdateEntry(): void {
		$this->runtestUpdateEntry();
	}

	/**
	 * Tests updating the draft field value with updateGfEntry.
	 */
	public function testUpdateDraft(): void {
		$this->runTestUpdateDraft();
	}

	/**
	 * Sets the correct Field Helper.
	 */
	public function field_helper() {
		return $this->tester->getPropertyHelper( 'PostImageField' );
	}

	/**
	 * Generates the form fields from factory. Must be wrappend in an array.
	 */
	public function generate_fields(): array {
		return [ $this->factory->field->create( $this->property_helper->values ) ];
	}

	/**
	 * The value as expected in GraphQL.
	 */
	public function field_value() {
		$field_value_input = $this->field_value_input();
		return [
			'altText'     => $field_value_input['altText'],
			'baseUrl'     => GFUtils::get_gravity_forms_upload_dir( $this->form_id )['url'] . '/',
			'caption'     => $field_value_input['caption'],
			'description' => $field_value_input['description'],
			'title'       => $field_value_input['title'],
			'url'         => GFUtils::get_gravity_forms_upload_dir( $this->form_id )['url'] . '/' . $field_value_input['image']['name'],
		];
	}

	public function draft_field_value() {
		$field_value = $this->field_value;
		unset( $field_value['baseUrl'] );

		return $field_value;
	}

	/**
	 * The graphql field value input.
	 */
	public function field_value_input() {
		return [
			'altText'     => 'someAlt',
			'caption'     => 'someCaption',
			'description' => 'someDesc',
			'title'       => 'someTitle',
			'image'       => [
				'name'     => 'img1.png',
				'type'     => 'image/png',
				'size'     => filesize( '/tmp/img1.png' ),
				'tmp_name' => '/tmp/img1.png',
			],
		];
	}

	/**
	 * Sets the value as expected by Gravity Forms.
	 */
	public function value() {
		$field_value = $this->field_value();
		$value       = implode( '|:|', [ $field_value['url'], $field_value['title'], $field_value['caption'], $field_value['description'], $field_value['altText'] ] );
		return [ $this->fields[0]['id'] => $value ];
	}

	/**
	 * The value as expected in GraphQL when updating from field_value().
	 */
	public function updated_field_value() {
		$field_value_input = $this->updated_field_value_input();
		return [
			'altText'     => $field_value_input['altText'],
			'caption'     => $field_value_input['caption'],
			'description' => $field_value_input['description'],
			'title'       => $field_value_input['title'],
			'baseUrl'     => GFUtils::get_gravity_forms_upload_dir( $this->form_id )['url'] . '/',
			'url'         => GFUtils::get_gravity_forms_upload_dir( $this->form_id )['url'] . '/' . $field_value_input['image']['name'],
		];
	}

	public function updated_draft_field_value() {
		$field_value = $this->updated_field_value();

		unset( $field_value['baseUrl'] );

		return $field_value;
	}

		/**
		 * The graphql field value input.
		 */
	public function updated_field_value_input() {
		return [
			'altText'     => 'upated Alt',
			'caption'     => 'upated Caption',
			'description' => 'upated Desc',
			'title'       => 'upated Title',
			'image'       => [
				'name'     => 'img2.png',
				'type'     => 'image/png',
				'size'     => filesize( '/tmp/img2.png' ),
				'tmp_name' => '/tmp/img2.png',
			],
		];
	}

	/**
	 * The GraphQL query string.
	 */
	public function field_query(): string {
		return '
			... on PostImageField {
				adminLabel
				allowedExtensions
				conditionalLogic {
					actionType
					logicType
					rules {
						fieldId
						operator
						value
					}
				}
				cssClass
				description
				descriptionPlacement
				errorMessage
				hasAlt
				hasCaption
				hasDescription
				hasTitle
				imageValues {
					altText
					baseUrl
					caption
					description
					filename
					title
					url
				}
				isFeaturedImage
				isRequired
				label
				labelPlacement
				personalData {
					isIdentificationField
					shouldErase
					shouldExport
				}
				subLabelPlacement
			}
		';
	}

	/**
	 * SubmitForm mutation string.
	 */
	public function submit_form_mutation(): string {
		return '
			mutation ($formId: ID!, $fieldId: Int!, $value: ImageInput!, $draft: Boolean) {
				submitGfForm( input: { id: $formId, saveAsDraft: $draft, fieldValues: {id: $fieldId, imageValues: $value}}) {
					errors {
						id
						message
						connectedFormField {
							databaseId
							type
						}
					}
					entry {
						formFields {
							nodes {
								... on PostImageField {
									imageValues {
										altText
										baseUrl
										caption
										description
										title
										url
									}
								}
							}
						}
						... on GfSubmittedEntry {
							databaseId
						}
						... on GfDraftEntry {
							resumeToken
						}
					}
				}
			}
		';
	}

	/**
	 * Returns the UpdateEntry mutation string.
	 */
	public function update_entry_mutation(): string {
		return '
			mutation updateGfEntry( $entryId: ID!, $fieldId: Int!, $value: ImageInput! ){
				updateGfEntry( input: { id: $entryId, shouldValidate: true, fieldValues: {id: $fieldId, imageValues: $value} }) {
					errors {
						id
						message
						connectedFormField {
							databaseId
							type
						}
					}
					entry {
						formFields {
							nodes {
								... on PostImageField {
									imageValues {
										altText
										baseUrl
										caption
										description
										title
										url
									}
								}
							}
						}
					}
				}
			}
		';
	}

	/**
	 * Returns the UpdateDraftEntry mutation string.
	 */
	public function update_draft_entry_mutation(): string {
		return '
			mutation updateGfDraftEntry( $resumeToken: ID!, $fieldId: Int!, $value: ImageInput! ){
				updateGfDraftEntry( input: {id: $resumeToken, idType: RESUME_TOKEN, shouldValidate: true, fieldValues: {id: $fieldId, imageValues: $value} }) {
					errors {
						id
						message
						connectedFormField {
							databaseId
							type
						}
					}
					entry: draftEntry {
						formFields {
							nodes {
								... on PostImageField {
									imageValues {
										altText
										baseUrl
										caption
										description
										title
										url
									}
								}
							}
						}
					}
				}
			}
		';
	}

	/**
	 * {@inheritDoc}
	 */
	public function expected_field_response( array $form ): array {
		$expected = $this->getExpectedFormFieldValues( $form['fields'][0] );

		$value = $this->is_draft ? $this->draft_field_value : $this->field_value;

		$expected[] = $this->expected_field_value(
			'imageValues',
			$value
		);

		return [
			$this->expectedObject(
				'gfEntry',
				[
					$this->expectedObject(
						'formFields',
						[
							$this->expectedNode(
								'nodes',
								$expected,
								0
							),
						]
					),
				]
			),
		];
	}

	/**
	 * The expected WPGraphQL mutation response.
	 *
	 * @param string $mutationName .
	 * @param mixed  $value .
	 */
	public function expected_mutation_response( string $mutationName, $value ): array {
		if ( empty( $value['url'] ) || $this->is_draft ) {
			unset( $value['url'] );
		}

		$expected[] = $this->expected_field_value(
			'imageValues',
			$value
		);

		return [
			$this->expectedObject(
				$mutationName,
				[
					$this->expectedObject(
						'entry',
						[
							$this->expectedObject(
								'formFields',
								[
									$this->expectedNode(
										'nodes',
										$expected,
										0
									),
								]
							),
						]
					),
				]
			),
		];
	}

	/**
	 * Checks if values submitted by GraphQL are the same as whats stored on the server.
	 *
	 * @param array $actual_entry .
	 * @param array $form .
	 */
	public function check_saved_values( $actual_entry, $form ): void {
		$ends_with = preg_replace( '/(.*?)gravity_forms\/(.*?)\/(.*?)/', '$3', $this->field_value['url'] );
		$this->assertStringContainsString( $ends_with, $actual_entry[ $form['fields'][0]['id'] ], 'Submit mutation entry id value not equal.' );
		$this->assertStringContainsString( $this->field_value['altText'], $actual_entry[ $form['fields'][0]['id'] ], 'Submit mutation entry altText value not equal.' );
		$this->assertStringContainsString( $this->field_value['caption'], $actual_entry[ $form['fields'][0]['id'] ], 'Submit mutation entry caption value not equal.' );
		$this->assertStringContainsString( $this->field_value['description'], $actual_entry[ $form['fields'][0]['id'] ], 'Submit mutation entry description value not equal.' );
		$this->assertStringContainsString( $this->field_value['title'], $actual_entry[ $form['fields'][0]['id'] ], 'Submit mutation entry Url value not equal.' );
	}

	public function mock_post_image_field( $field, $properties ) {
		if ( $field->type !== 'post_image' || $field instanceof FooPostImage ) {
			return $field;
		}

		return new FooPostImage( $properties );
	}
}
