<?php
namespace WpieFw\Settings\Export;

use WpieFw\Exceptions\WpieUnexpectedValueException;
use WpieFw\Settings\Iterator\WpieSetting;
use WpieFw\Settings\Iterator\WpieSettingsCollection;
use WpieFw\Helpers\WpieMultilangHelper;
use WpieFw\Wpie\WpieGlobals;

class WpieSettingsExporter implements WpieSettingsExporterInterface {

	/**
	 * @var WpieSettingsCollection
	 */
    private $settings;

    /**
     * @var WpieGlobals
     */
    private $globals;

    /**
     * Constructor
     *
     * @param WpieSettingsCollection $settings
     */
    public function __construct( WpieSettingsCollection $settings, WpieGlobals $globals )
    {
        $this->settings = $settings;
        $this->globals = $globals;
    }

    /**
     * Export the settings
     *
     * @return string base64 encoded string
     */
    public function export()
    {
        $arr = [];

        foreach( $this->settings as $name => $setting ) {
            if( $setting->isLanguaged() ) {
                $arr[$setting->getName()] = $setting->getStored();
            } else {
                $arr[$setting->getName()] = $setting->getStack();
            }
        }

        return base64_encode( maybe_serialize( $arr ) );
    }

    private function isIndexedBySettingName( array $settings = [] )
    {
        // check if settings are indexed by settings names
        $names = array_keys( $settings );
        foreach( $names  as $name) {
            if( !preg_match( sprintf( '/^%s_settings_[a-z_]+$/', $this->globals->nameSpace ), $name, $m ) ) {
                throw new WpieUnexpectedValueException( __( 'no valid settings for import', 'weepie' ) . ' (3)' );
            }
        }
    }

    /**
     * Import settings
     *
     * @throws WpieUnexpectedValueException when import data is not valid
     */
    public function import( $content = '' )
    {
        $decoded = base64_decode( $content );

        if( !$decoded ) {
            throw new WpieUnexpectedValueException( __( 'no valid settings for import', 'weepie' ) . ' (1)' );
        }

        $settings = maybe_unserialize( $decoded );

        if( false === $settings || !is_array( $settings ) || 0 === count( $settings ) ) {
            throw new WpieUnexpectedValueException( __( 'no valid settings for import', 'weepie' ) . ' (2)' );
        }

        // is the imported setting languaged
        $importFromLanguaged = WpieMultilangHelper::isLanguagedOption( array_values($settings)[0] );

        if( $importFromLanguaged ) {
            $importedFirstLocale = array_keys( array_values($settings)[0] )[0];
        }

        // new values to persist
        $toPersist = [];

        // current settings are leading
        foreach( $this->settings as $idx => $setting ) {
            // if setting is not in import, is not an array or empty array, continue
            if( !isset( $settings[$setting->getName()] )
                || !is_array( $settings[$setting->getName()] )
                || count( $settings[$setting->getName()] ) === 0 ) {
                continue;
            } else {
                $importedSetting = $settings[$setting->getName()];
                $importedStack = [];
            }

            if( $setting->isLanguaged() ) {
                $stored = $setting->getStored();

                // stored languaged settings are indexed by locale
                // try to import each locale stack from the imported setting
                foreach( $stored as $locale => $stack ) {
                    if( $importFromLanguaged ) {
                        // import locale as is
                        if( isset( $importedSetting[$locale] ) ) {
                            $importedStack[$locale] = $importedSetting[$locale];
                        // import sites default locale
                        } elseif( isset( $importedSetting[$this->globals->get( 'locale' )] ) ) {
                            $importedStack[$locale] = $importedSetting[$this->globals->get( 'locale' )];
                        // import imports first locale
                        } elseif( isset( $importedSetting[$importedFirstLocale] ) ) {
                            $importedStack[$locale] = $importedSetting[$importedFirstLocale];
                        }
                    } else {
                        // no languaged setting exist, import setting as stack for each locale
                        $importedStack[$locale] = $importedSetting;
                    }

                    // merge the current stack with the imported for each locale
                    $toPersist[$locale] = $this->mergeStacks( $stack, $importedStack[$locale] );
                }
            } else {
                // merge the current stack with the imported setting (stack)
                $stack = $setting->getStack();

                if( $importFromLanguaged ) {
                    $importedSetting = $importedSetting[$importedFirstLocale];
                }

                $toPersist = $this->mergeStacks( $stack, $importedSetting );
            }

            if( count( $toPersist ) > 0 ) {
                $setting->persistWith( $toPersist );
            }
        }
    }

    /**
     * Merge the old stack with the new stack
     */
    private function mergeStacks( $old, $new )
    {
        $merged = [];

        foreach( $old as $k => $v ) {
            if( isset( $new[$k] ) ) {
                $merged[$k] = $new[$k];
            } else {
                $merged[$k] = $v;
            }
        }

        return $merged;
    }
}