<?php

/**
 * English Stemmer
 *
 * Porter Stemmer algorithm implementation for English
 *
 * @package ProRank\SEO\Modules\Content\InternalLinking\Parser\MultiLanguage\Languages
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\Content\InternalLinking\Parser\MultiLanguage\Languages;

use ProRank\SEO\Modules\Content\InternalLinking\Parser\MultiLanguage\Stemmer;

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


/**
 * EnglishStemmer class
 */


class EnglishStemmer extends Stemmer {
    
    /**
     * Constructor
     */
    public function __construct() {
        parent::__construct('en');
    }
    
    /**
     * Stem a word using Porter Stemmer algorithm
     *
     * @param string $word Word to stem
     * @return string Stemmed word
     */
    public function stem(string $word): string {
        if (strlen($word) <= 2) {
            return $word;
        }
        
        $word = $this->normalize($word);
        
        // Step 1a
        $word = $this->step1a($word);
        
        // Step 1b
        $word = $this->step1b($word);
        
        // Step 1c
        $word = $this->step1c($word);
        
        // Step 2
        $word = $this->step2($word);
        
        // Step 3
        $word = $this->step3($word);
        
        // Step 4
        $word = $this->step4($word);
        
        // Step 5a
        $word = $this->step5a($word);
        
        // Step 5b
        $word = $this->step5b($word);
        
        return $word;
    }
    
    /**
     * Step 1a: Remove plural suffixes
     */
    private function step1a(string $word): string {
        if (substr($word, -4) === 'sses') {
            return substr($word, 0, -2);
        } elseif (substr($word, -3) === 'ies') {
            return substr($word, 0, -2);
        } elseif (substr($word, -2) === 'ss') {
            return $word;
        } elseif (substr($word, -1) === 's') {
            return substr($word, 0, -1);
        }
        return $word;
    }
    
    /**
     * Step 1b: Remove -ed, -ing
     */
    private function step1b(string $word): string {
        $flag = false;
        
        if (substr($word, -3) === 'eed') {
            $stem = substr($word, 0, -3);
            if ($this->get_measure($stem) > 0) {
                return substr($word, 0, -1);
            }
        } elseif (substr($word, -2) === 'ed') {
            $stem = substr($word, 0, -2);
            if ($this->contains_vowel($stem)) {
                $word = $stem;
                $flag = true;
            }
        } elseif (substr($word, -3) === 'ing') {
            $stem = substr($word, 0, -3);
            if ($this->contains_vowel($stem)) {
                $word = $stem;
                $flag = true;
            }
        }
        
        if ($flag) {
            if (substr($word, -2) === 'at' || substr($word, -2) === 'bl' || substr($word, -2) === 'iz') {
                $word .= 'e';
            } elseif ($this->double_consonant($word) && !preg_match('/[lsz]$/', $word)) {
                $word = substr($word, 0, -1);
            } elseif ($this->get_measure($word) === 1 && $this->cvc($word)) {
                $word .= 'e';
            }
        }
        
        return $word;
    }
    
    /**
     * Step 1c: Replace -y with -i
     */
    private function step1c(string $word): string {
        if (substr($word, -1) === 'y') {
            $stem = substr($word, 0, -1);
            if ($this->contains_vowel($stem)) {
                return $stem . 'i';
            }
        }
        return $word;
    }
    
    /**
     * Step 2: Map double suffixes to single ones
     */
    private function step2(string $word): string {
        $suffixes = [
            'ational' => 'ate',
            'tional' => 'tion',
            'enci' => 'ence',
            'anci' => 'ance',
            'izer' => 'ize',
            'abli' => 'able',
            'alli' => 'al',
            'entli' => 'ent',
            'eli' => 'e',
            'ousli' => 'ous',
            'ization' => 'ize',
            'ation' => 'ate',
            'ator' => 'ate',
            'alism' => 'al',
            'iveness' => 'ive',
            'fulness' => 'ful',
            'ousness' => 'ous',
            'aliti' => 'al',
            'iviti' => 'ive',
            'biliti' => 'ble',
        ];
        
        foreach ($suffixes as $suffix => $replacement) {
            if (substr($word, -strlen($suffix)) === $suffix) {
                $stem = substr($word, 0, -strlen($suffix));
                if ($this->get_measure($stem) > 0) {
                    return $stem . $replacement;
                }
            }
        }
        
        return $word;
    }
    
    /**
     * Step 3: Map double suffixes to single ones
     */
    private function step3(string $word): string {
        $suffixes = [
            'icate' => 'ic',
            'ative' => '',
            'alize' => 'al',
            'iciti' => 'ic',
            'ical' => 'ic',
            'ful' => '',
            'ness' => '',
        ];
        
        foreach ($suffixes as $suffix => $replacement) {
            if (substr($word, -strlen($suffix)) === $suffix) {
                $stem = substr($word, 0, -strlen($suffix));
                if ($this->get_measure($stem) > 0) {
                    return $stem . $replacement;
                }
            }
        }
        
        return $word;
    }
    
    /**
     * Step 4: Remove suffixes
     */
    private function step4(string $word): string {
        $suffixes = [
            'al', 'ance', 'ence', 'er', 'ic', 'able', 'ible',
            'ant', 'ement', 'ment', 'ent', 'ion', 'ou', 'ism',
            'ate', 'iti', 'ous', 'ive', 'ize'
        ];
        
        foreach ($suffixes as $suffix) {
            if (substr($word, -strlen($suffix)) === $suffix) {
                $stem = substr($word, 0, -strlen($suffix));
                if ($this->get_measure($stem) > 1) {
                    return $stem;
                }
            }
        }
        
        return $word;
    }
    
    /**
     * Step 5a: Remove -e
     */
    private function step5a(string $word): string {
        if (substr($word, -1) === 'e') {
            $stem = substr($word, 0, -1);
            $measure = $this->get_measure($stem);
            
            if ($measure > 1 || ($measure === 1 && !$this->cvc($stem))) {
                return $stem;
            }
        }
        return $word;
    }
    
    /**
     * Step 5b: Remove double consonant
     */
    private function step5b(string $word): string {
        if ($this->get_measure($word) > 1 && $this->double_consonant($word) && substr($word, -1) === 'l') {
            return substr($word, 0, -1);
        }
        return $word;
    }
    
    /**
     * Check if word ends with double consonant
     */
    private function double_consonant(string $word): bool {
        if (strlen($word) < 2) {
            return false;
        }
        
        $last = substr($word, -1);
        $second_last = substr($word, -2, 1);
        
        return $last === $second_last && !$this->is_vowel($last);
    }
    
    /**
     * Check if word ends with consonant-vowel-consonant
     */
    private function cvc(string $word): bool {
        if (strlen($word) < 3) {
            return false;
        }
        
        $last = substr($word, -1);
        $second_last = substr($word, -2, 1);
        $third_last = substr($word, -3, 1);
        
        return !$this->is_vowel($last) && 
               $this->is_vowel($second_last) && 
               !$this->is_vowel($third_last) &&
               !in_array($last, ['w', 'x', 'y']);
    }
    
    /**
     * Check if character is vowel
     */
    private function is_vowel(string $char): bool {
        return in_array($char, ['a', 'e', 'i', 'o', 'u']);
    }
}