<?php

namespace Drupal\Tests\image_replace\Functional;

use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Field\FieldConfigInterface;
use Drupal\Core\File\FileExists;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Image\ImageFactory;
use Drupal\Tests\BrowserTestBase;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\image\ImageStyleInterface;

/**
 * Tests functionality of the replace image effect.
 */
abstract class ImageReplaceTestBase extends BrowserTestBase {

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * Create a new image field.
   *
   * @param string $name
   *   The name of the new field (all lowercase), exclude the "field_" prefix.
   * @param string $type_name
   *   The node type that this field will be added to.
   * @param array{cardinality?: int} $storage_settings
   *   A list of field storage settings that will be added to the defaults.
   * @param array{required?: bool} $field_settings
   *   A list of instance settings that will be added to the instance defaults.
   * @param array<string, mixed> $widget_settings
   *   A list of widget settings that will be added to the widget defaults.
   */
  protected function createImageField(string $name, string $type_name, array $storage_settings = [], array $field_settings = [], array $widget_settings = []): FieldConfigInterface {
    FieldStorageConfig::create([
      'field_name' => $name,
      'entity_type' => 'node',
      'type' => 'image',
      'settings' => $storage_settings,
      'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
    ])->save();

    $field_config = FieldConfig::create([
      'field_name' => $name,
      'label' => $name,
      'entity_type' => 'node',
      'bundle' => $type_name,
      'required' => !empty($field_settings['required']),
      'settings' => $field_settings,
    ]);
    $field_config->save();

    $entity_display_repository = $this->container->get(EntityDisplayRepositoryInterface::class);
    assert($entity_display_repository instanceof EntityDisplayRepositoryInterface);
    $entity_display_repository->getFormDisplay('node', $type_name, 'default')
      ->setComponent($name, [
        'type' => 'image_image',
        'settings' => $widget_settings,
      ])
      ->save();

    $entity_display_repository->getViewDisplay('node', $type_name, 'default')
      ->setComponent($name)
      ->save();

    return $field_config;
  }

  /**
   * Create an image style containing the image replace effect.
   *
   * @param string $name
   *   The name of the new image style (all lowercase).
   */
  protected function createImageStyle(string $name): ImageStyleInterface {
    // Create an image style containing the replace effect.
    $style = ImageStyle::create([
      'name' => $name,
      'label' => $this->randomString(),
    ]);
    $effect = [
      'id' => 'image_replace',
      'data' => [],
    ];
    $style->addImageEffect($effect);
    $style->save();
    return $style;
  }

  /**
   * Create a pair of test files.
   *
   * @return array{\Drupal\file\FileInterface, \Drupal\file\FileInterface}
   *   An array with two file objects (original_file, replacement_file).
   */
  protected function createTestFiles(): array {
    // Generate test images.
    $file_system = $this->container->get(FileSystemInterface::class);
    assert($file_system instanceof FileSystemInterface);
    $mime_type_guesser = $this->container->get('file.mime_type.guesser');
    $original_uri = $file_system->copy(__DIR__ . '/fixtures/original.png', 'public://', FileExists::Rename);
    $this->assertTrue($this->imageIsOriginal($original_uri));
    $this->assertFalse($this->imageIsReplacement($original_uri));
    $original_file = File::create([
      'filename' => $file_system->basename($original_uri),
      'uri' => $original_uri,
      'status' => FileInterface::STATUS_PERMANENT,
      'filemime' => $mime_type_guesser->guessMimeType($original_uri),
    ]);
    $original_file->save();

    $replacement_uri = $file_system->copy(__DIR__ . '/fixtures/replacement.png', 'public://', FileExists::Rename);
    $this->assertTrue($this->imageIsReplacement($replacement_uri));
    $this->assertFalse($this->imageIsOriginal($replacement_uri));
    $replacement_file = File::create([
      'filename' => $file_system->basename($replacement_uri),
      'uri' => $replacement_uri,
      'status' => FileInterface::STATUS_PERMANENT,
      'filemime' => $mime_type_guesser->guessMimeType($replacement_uri),
    ]);
    $replacement_file->save();

    return [$original_file, $replacement_file];
  }

  /**
   * Returns TRUE if the image pointed at by the URI is the original image.
   */
  protected function imageIsOriginal(string $image_uri): bool {
    $expected_info = [
      'height' => 90,
      'mime_type' => 'image/png',
      'width' => 120,
    ];

    $image_factory = $this->container->get(ImageFactory::class);
    assert($image_factory instanceof ImageFactory);
    $image = $image_factory->get($image_uri);
    $image_info = [
      'height' => $image->getHeight(),
      'mime_type' => $image->getMimeType(),
      'width' => $image->getWidth(),
    ];

    // FIXME: Assert that original image has a red pixel on x=40, y=30.
    return $expected_info === $image_info;
  }

  /**
   * Returns TRUE if the image pointed at by the URI is the replacement image.
   */
  protected function imageIsReplacement(string $image_uri): bool {
    $expected_info = [
      'height' => 60,
      'mime_type' => 'image/png',
      'width' => 80,
    ];

    $image_factory = $this->container->get(ImageFactory::class);
    assert($image_factory instanceof ImageFactory);
    $image = $image_factory->get($image_uri);
    $image_info = [
      'height' => $image->getHeight(),
      'mime_type' => $image->getMimeType(),
      'width' => $image->getWidth(),
    ];

    // FIXME: Assert that replacement image has a green pixel on x=40, y=30.
    return $expected_info === $image_info;
  }

}
