<?php
namespace observers;
/**
 * Tagging observer
 * Implements the SplObserver interface
 * @author Jérôme Brilland
 * @version 3.0
 * @copyright (c) 2013-2021, Jérôme Brilland
 * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License
 */
class TaggingObserver implements \SplObserver {
    /*
     * @access protected
     */

    protected $tagnames = '';

    /**
     * Receive update from subject
     * @param SplSubject $subject
     * @param string $key
     * @return boolean
     * @access public
     */
    public function update( \SplSubject $subject, $key = null ) {
        if ( 'after_find' == $key ) {
            $database = \IoC::resolve( 'database' );
            $primaryKey = $subject->getPrimaryKey();
            $columns = ['tag.tag_id', 'name'];
            $join = ['[><]tagging' => 'tag_id'];
            $where = ['taggable_type=' => $subject->getTableName(), 'taggable_id' => $subject->$primaryKey];
            $subject->taggings = array_column( $database->select( 'tag', $join, $columns, $where ), 'name' );
            $subject->tagnames = implode( ', ', $subject->taggings );
        } else if ( 'before_save' == $key ) {
            $this->tagnames = $subject->tagnames;
            unset( $subject->tagnames );
        } else if ( 'after_save' == $key ) {
            $database = \IoC::resolve( 'database' );
            $tags = array_column( $database->select( 'tag', ['tag.tag_id', 'name'] ), 'name', 'tag_id' );

            if ( strlen( $this->tagnames ) > 0 ) {
                $tagnames = array_map( 'trim', explode( ',', $this->tagnames ) );
            } else {
                $tagnames = [];
            }
            $primaryKey = $subject->getPrimaryKey();
            $columns = ['tag.tag_id', 'name'];
            $join = ['[><]tagging' => 'tag_id'];
            $where = ['taggable_type=' => $subject->getTableName(), 'taggable_id' => $subject->$primaryKey];
            $result = array_column( $database->select( 'tag', $join, $columns, $where ), 'name', 'tag_id' );

            foreach ( array_diff( $tagnames, $result ) as $tagname ) {
                if ( in_array( $tagname, $tags ) ) {
                    $tagId = array_search( $tagname, $tags );
                } else {
                    $database->insert( 'tag', ['name' => $tagname, 'created_at' => date( 'Y-m-d H:i:s' )] );
                    $error = $database->error();
                    if ( $error[1] ) {
                        throw new \ErrorException( $error[2] );
                    }
                    $tagId = $database->id();
                }
                $database->insert( 'tagging', ['tag_id' => $tagId, 'taggable_type' => $subject->getTableName(), 'taggable_id' => $subject->$primaryKey] );
            }

            foreach ( array_diff( $result, $tagnames ) as $tagname ) {
                $where = ['tag_id' => array_keys( $tags, $tagname ), 'taggable_type' => $subject->getTableName(), 'taggable_id' => $subject->$primaryKey];
                $database->delete( 'tagging', $where );
                $error = $database->error();
                if ( $error[1] ) {
                    throw new \ErrorException( $error[2] );
                }
            }
            $this->removeUnusedTags();
        }
        return true;
    }

    private function removeUnusedTags() {
        $database = \IoC::resolve( 'database' );
        $columns = ['tag_id', 'tagging_id'];
        $join = ['[>]tagging' => 'tag_id'];
        $where = ['tagging_id' => null];
        foreach ( array_column( $database->select( 'tag', $join, $columns, $where ), 'tag_id' ) as $tagId ) {
            $database->delete( 'tag', ['tag_id' => $tagId] );
        }
        $error = $database->error();
        if ( $error[1] ) {
            throw new \ErrorException( $error[2] );
        }
    }

}

