<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_node_processor\Unit\Plugin\FlowDropNodeProcessor;

use Drupal\flowdrop\DTO\ParameterBag;
use Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor\MarkdownToHtml;
use PHPUnit\Framework\TestCase;

/**
 * Tests the MarkdownToHtml node processor.
 *
 * Tests verify the HTML output of both the CommonMark library path and the
 * regex fallback path. The plugin selects the path automatically via
 * class_exists('League\CommonMark\CommonMarkConverter'), so tests assert
 * semantic equivalence rather than byte-identical output.
 *
 * @coversDefaultClass \Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor\MarkdownToHtml
 * @group flowdrop_node_processor
 */
class MarkdownToHtmlTest extends TestCase {

  /**
   * Creates a MarkdownToHtml processor instance.
   *
   * @return \Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor\MarkdownToHtml
   *   The processor instance.
   */
  private function createProcessor(): MarkdownToHtml {
    return new MarkdownToHtml([], 'markdown_to_html', ['id' => 'markdown_to_html']);
  }

  /**
   * Tests H1 heading conversion.
   *
   * @covers ::process
   */
  public function testH1Heading(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "# Main Title\n",
    ]));

    $this->assertStringContainsString('<h1>', $result['html']);
    $this->assertStringContainsString('Main Title', $result['html']);
  }

  /**
   * Tests H2 heading conversion.
   *
   * @covers ::process
   */
  public function testH2Heading(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "## Section Title\n",
    ]));

    $this->assertStringContainsString('<h2>', $result['html']);
    $this->assertStringContainsString('Section Title', $result['html']);
  }

  /**
   * Tests bold text conversion.
   *
   * @covers ::process
   */
  public function testBoldText(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "**bold content**",
    ]));

    $this->assertStringContainsString('<strong>', $result['html']);
    $this->assertStringContainsString('bold content', $result['html']);
  }

  /**
   * Tests italic text conversion.
   *
   * @covers ::process
   */
  public function testItalicText(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "*italic content*",
    ]));

    $this->assertStringContainsString('<em>', $result['html']);
    $this->assertStringContainsString('italic content', $result['html']);
  }

  /**
   * Tests fenced code block conversion.
   *
   * @covers ::process
   */
  public function testFencedCodeBlock(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "```php\necho 'hello';\n```\n",
    ]));

    $this->assertStringContainsString('<code', $result['html']);
    $this->assertStringContainsString("echo", $result['html']);
  }

  /**
   * Tests link conversion.
   *
   * @covers ::process
   */
  public function testLinkConversion(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "[Example](https://example.com)",
    ]));

    $this->assertStringContainsString('<a ', $result['html']);
    $this->assertStringContainsString('https://example.com', $result['html']);
    $this->assertStringContainsString('Example', $result['html']);
  }

  /**
   * Tests that dangerous protocols are stripped from links.
   *
   * @covers ::process
   */
  public function testDangerousProtocolStripped(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "[Click](javascript:alert(1))",
    ]));

    $this->assertStringNotContainsString('javascript:', $result['html']);
  }

  /**
   * Tests strip_h1 parameter removes the first H1 heading.
   *
   * @covers ::process
   */
  public function testStripH1RemovesFirstHeading(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "# The Title\n\nBody content.",
      'strip_h1' => TRUE,
    ]));

    $this->assertStringNotContainsString('<h1>', $result['html']);
    $this->assertStringContainsString('Body content', $result['html']);
  }

  /**
   * Tests that strip_h1 does not affect H2+ headings.
   *
   * @covers ::process
   */
  public function testStripH1DoesNotAffectH2(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "# Title\n\n## Section\n\nContent.",
      'strip_h1' => TRUE,
    ]));

    $this->assertStringContainsString('<h2>', $result['html']);
    $this->assertStringContainsString('Section', $result['html']);
  }

  /**
   * Tests that strip_h1 defaults to FALSE.
   *
   * @covers ::process
   */
  public function testStripH1DefaultsFalse(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "# My Title\n\nContent.",
    ]));

    $this->assertStringContainsString('<h1>', $result['html']);
    $this->assertStringContainsString('My Title', $result['html']);
  }

  /**
   * Tests that raw output equals the original input before strip_h1.
   *
   * @covers ::process
   */
  public function testRawIsOriginalInput(): void {
    $processor = $this->createProcessor();
    $input = "# Title\n\nContent.";
    $result = $processor->process(new ParameterBag([
      'text' => $input,
      'strip_h1' => TRUE,
    ]));

    $this->assertSame($input, $result['raw']);
  }

  /**
   * Tests that empty input returns empty html.
   *
   * @covers ::process
   */
  public function testEmptyInputReturnsEmptyHtml(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag(['text' => '']));

    $this->assertIsString($result['html']);
    $this->assertSame('', trim($result['html']));
  }

  /**
   * Tests that HTML special characters in headings are escaped.
   *
   * @covers ::process
   */
  public function testHeadingContentIsEscaped(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "## Hello <script>alert(1)</script>\n",
    ]));

    $this->assertStringNotContainsString('<script>', $result['html']);
  }

  /**
   * Tests horizontal rule conversion.
   *
   * @covers ::process
   */
  public function testHorizontalRule(): void {
    $processor = $this->createProcessor();
    $result = $processor->process(new ParameterBag([
      'text' => "---\n",
    ]));

    $this->assertStringContainsString('<hr', $result['html']);
  }

}
