<?php

use Themeco\Cornerstone\Util\Networking;

/**
 * This class exists to manage a persistent index
 * of which items have been imported already.
 *
 * This ensures items are not duplicated, and that if a
 * user modifies demo content it will not be overwritten.
 */
class Cornerstone_Site_Import_Registry {

  public $namespace;
  public $registry;

  /**
   * Provision this instance with a namespace and populate
   * registry with any existing values.
   * @param string $namespace Unique namespace
   * @return void
   */
  public function setNameSpace( $namespace ) {
    $this->namespace = $namespace;
    $option = get_option('x_design_cloud_site_registry', array() );
    $this->registry = isset($option[$namespace]) ? $option[$namespace] : array();
  }

  /**
   * Persist data in our namespace to a WP option.
   * @return void
   */
  public function save() {
    $option = get_option('x_design_cloud_site_registry', array() );
    $option[$this->namespace] = $this->registry;
    update_option( 'x_design_cloud_site_registry', $option );
  }

  /**
   * Get an existing value if available
   * @param  string $group Group to check for ID
   * @param  string $key    Hash based ID provided by XDE
   * @return mixed         null if it doesn't exist.
   */
  public function get( $group, $key ) {
    return ( isset( $this->registry[$group] ) &&  isset( $this->registry[$group][$key] ) )
          ? $this->registry[$group][$key] : null;
  }

  /**
   * Returns everything stored in this registry.
   * @return array
   */
  public function all() {
    return $this->registry;
  }

  /**
   * Test if a value exists
   * @param  string $group Group to check for key
   * @param  string $key    Hash based ID provided by XDE
   * @return bool         true/false based on existence
   */
  public function exists( $group, $key ) {
    return (!is_null( $this->get( $group, $key ) ) );
  }

  /**
   * Add a value to our registry
   * @param  string  $group     Group to place the ID in the registry
   * @param  string  $key       unique ID string
   * @param  mixed   $value     Store a reference to an ID or entity on this install
   * @param  boolean $overwrite should existing values be allowed to be overwritten
   * @return boolean            true, otherwise false if id exists
   */
  public function set( $group, $key, $value, $overwrite = true ) {

    if ( !$overwrite && $this->exists( $group, $key ) )
      return false;

    if (!isset($this->registry[$group]))
      $this->registry[$group] = array();

    $this->registry[$group][$key] = $value;
    $this->save();

    return true;
  }

  /**
   * Delete a value from the registry
   * @param  string $group Group to delete a key from.
   * @param  string $key    Hash based ID provided by XDE
   * @return void
   */
  public function delete( $group, $key ) {

    if ( $this->exists( $group, $key ) )
      unset( $this->registry[$group][$key] );

    $this->save();

  }
  /**
   * Deletes a key from a group in all namespaces
   * @param  string $group Group to delete a key from.
   * @param  string $key    Hash based ID provided by XDE
   * @return void
   */
  public static function deleteAll( $group, $key ) {
    $option = get_option('x_design_cloud_site_registry', array() );
    $namespaces = array_keys( $option );
    foreach ( $namespaces as $namespace ) {
      $registry = new Cornerstone_Site_Import_Registry;
      $registry->setNameSpace( $namespace );
      $registry->delete( $group, $key );
    }
  }

}

class Cornerstone_Site_Import_Processor {

  public $batchCount = 0;
  public $maxBatchSize = 2;
  public $registry;
  private $session;
  private $namespace;
  private $jobs_total = 0;
  private $message = '';
  private $jobs = null;
  private $debugMessage = '';

  public function setup( $session ) {

    $this->maxBatchSize = apply_filters( 'x_demo_batch_size', $this->maxBatchSize );

    $this->session = $session;
    $this->namespace = $this->session->get( 'namespace' );
    $this->registry = new Cornerstone_Site_Import_Registry;
    $this->registry->setNameSpace( $this->namespace );

    $this->jobs = $this->session->get('jobs');
    $this->jobs_total = $this->session->get('jobs_total');


    if ( is_null( $this->jobs ) ) {

      $this->jobs = $this->session->get('demo');

      $sliders = $this->session->get('sliders');

      foreach ($sliders as $name => $url) {
        array_unshift( $this->jobs, array(
          'task' => 'Slider',
          'data' => array(
            'name' => $name,
            'url' => $url
        ) ) );
      }

      array_unshift( $this->jobs, array( 'task' => 'Cleanup', 'data' => array() ) );
      $this->jobs_total = count( $this->jobs );

    }

  }

  /**
   * Process the next job in the queue
   */
  public function nextJob() {

    $this->debugMessage = '';

    if ( 0 == count( $this->jobs ) )
      return true;

    // Get the next job
    $job = array_shift( $this->jobs );

    // Ensure an import method exists for the requested task
    $methodName = 'import' . $job['task'];

    if ( !method_exists( $this, $methodName ) ) {
      return new WP_Error( 'cornerstone', 'Task does not have an import method: ' . $job['task'] );
    }

    // Run the job
    $run = call_user_func_array( array( $this, $methodName ), array( $job['data'] ) );
    if ( is_wp_error( $run ) )
      return $run;


    $this->session->set( 'registry', $this->registry->all() );
    $this->save();
    $this->registry->save();

    if ( false === $run ) {
      $this->batchCount++;
      return $this->nextJob();
    }

    if ( $run != 'next' && $this->batchCount <= $this->maxBatchSize ) {
      $this->batchCount++;
      return $this->nextJob();
    } else {
      $this->batchCount = 0;
      return $run;
    }

  }


  public function message() {
    return $this->message;
  }

  public function debugMessage() {
    return $this->debugMessage;
  }

  /**
   * Get a percentage of completion
   * @return int 0-1 value
   */
  public function completion() {

    if ( empty( $this->jobs ) )
      return true;

    return array(
      'total' => $this->jobs_total,
      'remaining' => count($this->jobs),
      'ratio' => ( $this->jobs_total - count($this->jobs) ) / $this->jobs_total
    );

  }

  /**
   * Save our progress in the session.
   * Only persists when the session itself is saved
   * @return void;
   */
  public function save() {
    $this->session->set( 'jobs_total', $this->jobs_total );
    $this->session->set( 'jobs', $this->jobs );
  }

  /**
   * Job handler for cleanup.
   * This deletes any unmodified pages imported from other demos
   * @param  array $data unused
   * @return void
   */
  public function importCleanup( $data ) {

    global $wpdb;
    $cleanup = $wpdb->get_results( "SELECT p.ID,m.meta_value FROM {$wpdb->posts} AS p INNER JOIN {$wpdb->postmeta} AS m ON m.post_id = p.ID AND m.meta_key = 'x_demo_content'" );

    foreach ($cleanup as $item) {

      $split = explode( '|', $item->meta_value );

      if ( count( $split) != 2 || $this->namespace == $split[0] )
        continue;

      Cornerstone_Site_Import_Registry::deleteAll( 'post', $split[1] );
      wp_delete_post( $item->ID, 'x_demo_content' );

    }

    $this->message = __( 'Initializing...', 'cornerstone');
    return 'next';
  }


  public function importSlider( $data ) {


    if (!class_exists('RevSliderSlider'))
      return false;



    if ( RevSliderSlider::isAliasExists( $data['name'] ) )
      return false;

    ob_start();

    try {

      require_once(RS_PLUGIN_PATH . 'admin/includes/template.class.php');
      require_once(RS_PLUGIN_PATH . 'admin/includes/import.class.php');

      $slider = new RevSliderSlider;

      if ( ! function_exists( 'request_filesystem_credentials' ) ) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
      }
      ob_start();
      $creds = request_filesystem_credentials( '', '', false, false, null );
      ob_get_clean();

      if ( ! WP_Filesystem( $creds ) ) {
        return new WP_Error( 'cornerstone', 'Unable to use file system.' );
      }

      global $wp_filesystem;

      $dir = wp_upload_dir();
      $file = trailingslashit( $dir['basedir'] )  . basename( $data['url'] );

      $response = wp_remote_get( $data['url'] );

      $wp_filesystem->put_contents(
        $file,
        $response['body'],
        FS_CHMOD_FILE // predefined mode settings for WP files
      );

      $slider->importSliderFromPost( true, true, $file );

      if ( file_exists( $file ) ) {
        wp_delete_file( $file );
      }

    } catch (Exception $e) {
      return new WP_Error( 'cornerstone', $e->getMessage() );
    }

    $this->debugMessage = ob_get_clean();

    $this->message = __('Revolution Slider downloaded...', 'cornerstone');
    return 'text';
  }

  public function importImage( $data ) {

    if ( $this->registry->exists( 'image', $data['id'] ) ) {
      return false;
    }

    if ( ! function_exists( 'download_url' ) ) {
      require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    $download = download_url( $data['url'], 30 );

    // Hotlink images that fail to download (usually this means deleted images)
    if ( is_wp_error( $download ) ) {
      $this->registry->set( 'image', $data['id'], $data['url'] );
      $this->message = sprintf( __('Downloading images...', 'cornerstone'), basename( $data['url'] ) );
      return true;
    }

    $file = array( 'name' => basename($data['url']), 'tmp_name' => $download );
    $results = wp_handle_sideload( $file, array( 'test_form' => false ) );

    if ( !empty( $results['error'] ) )
      return new WP_Error('cornerstone', 'Failed to sideload image: ' + $data['url'] );


    $name  = explode( '.', basename( $results['file'] ) );

    $newPost = wp_insert_attachment( array(
      'post_title' => sanitize_file_name( $name[0]),
      'post_content' => '',
      'post_type' => 'attachment',
      'post_mime_type' => $results['type'],
      'guid' => $results[ 'url' ]
    ), $results['file'] );

    if ( is_wp_error( $newPost ) )
      return $newPost;

    if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
      require_once ABSPATH . 'wp-admin/includes/image.php';
    }
    wp_update_attachment_metadata( $newPost, wp_generate_attachment_metadata( $newPost, $results['file'] ) );

    $results['post_id'] = (int) $newPost;

    $this->registry->set( 'image', $data['id'], $results );

    $this->message = sprintf( __('Downloading images...', 'cornerstone'), basename( $results['url'] ) );
    return true;
  }

  public function updateTermMeta( $term_id, $meta ) {

    $option = 'taxonomy_' . $term_id;

    $term_meta = get_option( $option, array() );

    if ( is_array( $meta ) ) {
      foreach ($meta as $key => $value) {
        $term_meta[$key] = $value;
      }
    }

    update_option( $option, $term_meta );

  }

  public function importTerm( $term ) {

    if ( $term['taxonomy'] == 'nav_menu' )
      return new WP_Error( 'cornerstone', 'Term importer should not be used to process menus.' );

    $parent = $term['parent'];

    if ( $parent != 0 ) {
      $parent = $this->registry->get( 'term', $term['parent'] );
      if ( is_null( $parent ) )
        return new WP_Error( 'cornerstone', 'Export file has hierarchical terms out of order. ' . json_encode($term));

    }

    $this->message = sprintf( __( 'Preparing taxonomies...', 'cornerstone' ), $term['name'] );

    $existing_term = get_term_by( 'name', $term['name'], $term['taxonomy'] );

    if ( $existing_term != false ) {

      $term_id = (int) $existing_term->term_id;

      wp_update_term( $term_id, $term['taxonomy'], array(
        'description' => $term['description'],
        'slug' => $term['slug'],
        'parent' => $parent
      ));

      $this->updateTermMeta( $term_id, $term['meta'] );
      $this->registry->set( 'term', $term['term_id'], $term_id );

      return true;
    }

    $newTerm = wp_insert_term( $term['name'], $term['taxonomy'], array(
      'description' => $term['description'],
      'slug' => $term['slug'],
      'parent' => $parent
    ));

    if ( is_wp_error( $newTerm ) )
      return $newTerm;

    $this->updateTermMeta( $newTerm['term_id'], $term['meta'] );

    $this->registry->set( 'term', $term['term_id'], $newTerm['term_id'] );

    return true;
  }

  public function createAttachment( $post, $data ) {

      if ( !isset( $post['attachment_url'] ) ) {
        $this->debugMessage = "Attachment missing attachment_url: " . $post['ID'];
        return false;
      }

      $imageData = $this->registry->get( 'image', $this->findFirstImageID( $post['attachment_url'] ) );

      if ( is_null($imageData)) {
        $this->debugMessage = "Attachment not found in registry: " . $post['ID']. " | $id";
        return false;
      }

      $this->registry->set( 'post', $post['ID'], (int) $imageData['post_id'] );

      $this->message = sprintf( __( 'Attaching thumbnails...', 'cornerstone' ), $post['post_title'] );
      return true;
  }

  public function importPost( $post ) {

    if ( $post['post_type'] == 'nav_menu_item' )
      return new WP_Error( 'cornerstone', 'Post importer should not be used to process menu items.' );

    if ( $this->registry->get( 'post', $post['ID'] ) ) {

      if ( ! is_null( get_post( $post['ID'] ) ) ) {
        $this->skipPostMeta( $post['ID'] );
        return false;
      }

      $this->registry->delete( 'post', $post['ID'] );

    }

    $newPost = array();

    // Set pass-through keys. Strip anything set later.
    foreach ($post as $key => $value) {
      if (!in_array($key, array( 'ID', 'post_parent', 'post_content', 'terms', 'post_meta' ) ) )
        $newPost[$key] = $value;
    }

    if ( $post['post_type'] == 'attachment' )
      return $this->createAttachment( $post, $newPost );

    if ( $post['post_type'] === 'cs_global_block' ) {
      $post['post_status'] = 'tco-data';
    }

    // Set content
    $newPost['post_content'] = ( isset( $post['post_content'] ) ) ? $this->normalizeContent( $post['post_content'] ) : '';

    // Set Parent
    $parent = ( isset( $post['post_parent'] ) ) ? $post['post_parent'] : 0;

    if ( $parent != 0 ) {
      $parent = $this->registry->get( 'post', $post['post_parent'] );
      if ( is_null( $parent ) )
        return new WP_Error( 'cornerstone', 'Export file has hierarchical posts out of order. ');

    }

    $newPost['post_parent'] = $parent;

    // Create it!
    $newPost = wp_insert_post( $newPost, true );

    if ( is_wp_error( $newPost ) )
      return $newPost;

    // Add terms to this post
    foreach ( $post['terms'] as $taxonomy => $termIDs ) {

      $newTermIDs = array();

      foreach ($termIDs as $termID) {
        $newTermIDs[] = (int) $this->registry->get( 'term', $termID );
      }


      $taxTermIDs = wp_set_object_terms( $newPost, $newTermIDs, $taxonomy );

      if ( is_wp_error( $taxTermIDs ) )
        return $taxTermIDs;
    }

    $this->registry->set( 'post', $post['ID'], (int) $newPost );

    $this->message = sprintf( __( 'Creating posts...', 'cornerstone' ), $post['post_title'] );
    return true;

  }

  public function skipPostMeta( $id ) {
    $skipped = $this->session->get( 'skipped_post_meta' );
    if ( !is_array( $skipped ) )
      $skipped = array();
    $skipped[] = $id;
    $this->session->set( 'skipped_post_meta', $skipped );
  }

  public function isPostMetaSkipped( $id ) {
    $skipped = $this->session->get( 'skipped_post_meta' );
    return ( is_array( $skipped ) && in_array( $id, $skipped ) );
  }

  public function importPostMeta( $data ) {

    if ( $this->isPostMetaSkipped( $data['id'] ) ) {
      return false;
    }

    $postID = $this->registry->get( 'post', $data['id'] );

    $post = get_post( $postID );

    if ( is_null( $post ) ) {
      return new WP_Error('cornerstone', "Could not locate post: $postID | " . $data['id'] );
    }

    if ( is_wp_error( $post ) ) {
      return $post;
    }

    foreach ($data['meta'] as $key => $value) {

      // Specific cases
      if ( '_thumbnail_id' == $key ) {
        if ( false === set_post_thumbnail( $post->ID, $this->registry->get( 'post', $value ) ) )
          $this->debugMessage = "Could not set thumbnail ($key) to post: {$post->ID} | attachment: $value";
        continue;
      }

      // General purpose
      $newValue = $this->normalizeContent( maybe_unserialize( $value ) );
      $maybe_json = is_string( $newValue ) ? json_decode( $newValue ) : null;

      if ( ! is_null( $maybe_json ) ) {
        // Dont allow empty json
        // Some revisions are empty
        // not sure why but this is the hack
        if (empty($maybe_json)) {
          continue;
        }

        // JSON needs to be slashed before storing
        $newValue = wp_slash( $newValue );
      }

      // Skip existing values
      if ( get_post_meta( $post->ID, $key, $value ) == $newValue ) {
        continue;
      }

      if ( false === update_post_meta( $post->ID, $key, $newValue ) ) {
        $this->debugMessage = "Could not add meta key ($key) to post: {$post->ID}";
      }

      if ($key === '_cornerstone_data') {
        // This is causing issues
        // @TODO figure out if needed
        //cornerstone('Resolver')->getDocument( $post )->save();
      }

    }

    // Flag this as disposable demo content.
    update_post_meta( $post->ID, 'x_demo_content', $this->namespace . '|' . $data['id'] );

    $this->message = sprintf( __( 'Adding post data...', 'cornerstone' ), $post->post_title );
    return true;

  }

  public function importMenu( $menu ) {

    wp_delete_nav_menu( $menu['menu-name'] );

    $newMenu = wp_update_nav_menu_object( 0, $menu );

    if ( is_wp_error( $newMenu ) ) {
      return $newMenu;
    }

    $this->registry->set( 'term', $menu['id'], (int) $newMenu );

    foreach ( $menu['items'] as $menuItem ) {

      $existing = $this->registry->get( 'post', $menuItem['id'] );

      if ( is_nav_menu_item( $existing ) ) {
        wp_delete_post( $existing, true );
      }

      if ( 'taxonomy' == $menuItem['menu-item-type'] ) {
        $menuItem['menu-item-object-id'] = $this->registry->get( 'term', $menuItem['menu-item-object-id'] );
      } elseif ( 'post_type' == $menuItem['menu-item-type'] ) {
        $menuItem['menu-item-object-id'] = $this->registry->get( 'post', $menuItem['menu-item-object-id'] );
      }


      if ( 0 != (int) $menuItem['menu-item-parent-id'] ) {
        $menuItem['menu-item-parent-id'] = $this->registry->get( 'post', $menuItem['menu-item-parent-id'] );
        if ( is_null( $menuItem['menu-item-parent-id'] ) ) {
          continue;
        }
      }

      $newMenuItem = wp_update_nav_menu_item( $newMenu, 0, $menuItem );

      if ( is_wp_error( $newMenuItem ) ) {
        return $newMenuItem;
      }

      $this->registry->set( 'post', $menuItem['id'], (int) $newMenuItem );

    }

    $this->message = sprintf( __( 'Creating menus...', 'cornerstone' ), $menu['menu-name'] );
    return true;
  }

  public function importSidebar( $data ) {

    $sidebars_widgets = get_option('sidebars_widgets');

    foreach ($data as $sidebar => $widgets) {

      // Move existing widgets to inactive
      if( isset( $sidebars_widgets[$sidebar] ) && !empty($sidebars_widgets[$sidebar] ) ) {

        if ( !isset( $sidebars_widgets['wp_inactive_widgets'] ) ) {
          $sidebars_widgets['wp_inactive_widgets'] = array();
        }

        foreach ( $sidebars_widgets[$sidebar] as $widget_id ) {
          $sidebars_widgets['wp_inactive_widgets'][] = $widget_id;
        }

        $sidebars_widgets[$sidebar] = array();

      }

      // Derived from https://github.com/stevengliebe/widget-importer-exporter
      foreach ($widgets as $widget) {

        if ( $widget['type'] == 'nav_menu' ) {
          $widget['meta']['nav_menu'] = $this->registry->get( 'term', $widget['meta']['nav_menu'] );
        }

        if ( $widget['type'] == 'text' ) {
          $widget['meta']['text'] = $this->normalizeContent( $widget['meta']['text'] );
        }

        $default = array( '_multiwidget' => 1 );
        $allWidgets = get_option( 'widget_' . $widget['type'], $default ); // all instances for that widget ID base, get fresh every time
        if ( empty( $allWidgets ) ) {
          $allWidgets = $default;
        }

        $allWidgets[] = $widget['meta'];

        end( $allWidgets );
        $newId = key( $allWidgets );

        if ( '0' === strval( $newId ) ) {
          $newId = 1;
          $allWidgets[$newId] = $allWidgets[0];
          unset( $allWidgets[0] );
        }

        if ( isset( $allWidgets['_multiwidget'] ) ) {
          $multiwidget = $allWidgets['_multiwidget'];
          unset( $allWidgets['_multiwidget'] );
          $allWidgets['_multiwidget'] = $multiwidget;
        }

        update_option( 'widget_' . $widget['type'], $allWidgets );

        $sidebars_widgets[$sidebar][] = $widget['type'] . '-' . $newId;

      }

    }

    update_option( 'sidebars_widgets', $sidebars_widgets );

    $this->message = 'Preparing sidebars...';
    return true;
  }


  public function importOptions( $data ) {

    if ( isset( $data['page_on_front'] ) ) {

      $postID = $key = $this->registry->get( 'post', $data['page_on_front'] );

      if ( !is_null( $postID ) ) {
        update_option('page_on_front', $postID );
      }

      unset($data['page_on_front']);
    }

    if ( isset( $data['page_for_posts'] ) ) {

      $postID = $this->registry->get( 'post', $data['page_for_posts'] );

      if ( !is_null( $postID ) ) {
        update_option('page_for_posts', $postID );
      }

      unset($data['page_for_posts']);
    }

    if ( isset( $data['cornerstone_font_items'] ) ) {

      update_option(
        'cornerstone_font_items',
        wp_slash( cs_json_encode( $this->mergeOptionModels(
          get_option('cornerstone_font_items', []),
          $data['cornerstone_font_items']
        ) ) )
      );

      unset($data['cornerstone_font_items']);
    }

    if ( isset( $data['cornerstone_color_items'] ) ) {

      update_option(
        'cornerstone_color_items',
        wp_slash( cs_json_encode( $this->mergeOptionModels(
          get_option('cornerstone_color_items', []),
          $data['cornerstone_color_items']
        ) ) )
      );

      unset($data['cornerstone_color_items']);
    }


    // Loop through theme options to use
    // update_option
    foreach ($data as $key => $value) {
      $normalized = $this->normalizeContent(maybe_unserialize($value));

      // Font weight migration
      if (strpos($key, "font_weight_selection") !== false) {
        $normalized = apply_filters( 'cs_migrate_font_weight', $normalized );
      }

      // Font family migration
      if (strpos($key, "font_family_selection") !== false) {
        $normalized = apply_filters( 'cs_migrate_font_family', $normalized );
      }

      if ( false === update_option( $key, $normalized ) && get_option($key) != $normalized) { // intentional usage of non-strict equality

        if ( ! $this->debugMessage ) {
          $this->debugMessage = '';
        }
        $this->debugMessage .= "Could not set option ($key): {$value} | {$normalized} ";
      }
    }

    // Legacy sites never had this
    // false doesn't get updated if it was never set
    // So we change to non false back to false, so the entry is actually in the database
    // https://developer.wordpress.org/reference/functions/update_option/#source
    update_option('x_enable_font_manager', '0', true);
    update_option('x_enable_font_manager', false, true);

    $this->message = __( 'Setting Theme Options...', 'cornerstone' );

    do_action( 'cs_theme_options_after_save' );

    return true;
  }

  function parseOptionModel( $data ) {
    $json = is_array($data) ? $data : json_decode( wp_unslash( $data ), true );
    if (!is_array($json)) {
      return [];
    }
    $result = [];
    foreach($json as $item) {
      $result[$item['_id']] = $item;
    }
    return $result;
  }

  function mergeOptionModels() {
    $args = func_get_args();
    $decoded = array_map( array( $this, 'parseOptionModel' ), $args );
    $merged = array_reduce($decoded, 'array_merge', array());
    return array_values( $merged );
  }

  public function importThemeMods( $mods ) {

    if (isset($mods['nav_menu_locations'])) {
      remove_theme_mod('nav_menu_locations');
      $normalizedLocations = array();
      foreach ( $mods['nav_menu_locations'] as $location => $id ) {
        $normalizedLocations[$location] = $this->registry->get( 'term', $id );
      }
      $mods['nav_menu_locations'] = $normalizedLocations;
    }

    foreach ($mods as $key => $value) {
      set_theme_mod( $key, $value);
    }

    $this->message = __( 'Assigning menus...', 'cornerstone' );
    return true;
  }

  public function normalizeContent( $content ) {

    if ( is_array( $content ) ) {
      return array_map( array( $this, 'normalizeContent'), $content );
    }

    if ( !is_string( $content ) ) {
      return $content;
    }

    // Restore site URLs
    $content = str_replace( '{{xde:site:url}}', trailingslashit( home_url() ) , $content );

    // Restore Global Block IDs
    $content = preg_replace_callback( '#{{xde:global_block:([\w\d]+)}}#', array( $this, 'restoreGlobalBlockID' ), $content );

    return $this->normalizeImageURLs( $content );
  }

  public function restoreGlobalBlockID( $matches ) {
    return $this->registry->get('post', $matches[1]);
  }

  public function findImageIDs( $content ) {

    if ( !is_string( $content ) ) {
      return $content;
    }

    preg_match_all('#(?<={{img:).*?(?=:img}})#', $content, $matches);

    $ids = array();

    foreach ($matches[0] as $id) {
      $ids[] = $id;
    }

    return $ids;
  }

  public function findFirstImageID( $content ) {
    $ids = $this->findImageIDs( $content );
    return $ids[0];
  }

  public function normalizeImageURLs( $content ) {

    $ids = $this->findImageIDs( $content );

    foreach ($ids as $id) {
      $imageData = $this->registry->get( 'image', $id );
      if ( !is_null( $imageData) && isset( $imageData['url'] ) ) {
        $content = str_replace( '{{img:' . $id . ':img}}', $imageData['url'] , $content );
      }
    }

    return $content;
  }

}


class Cornerstone_Controller_Design_Cloud extends Cornerstone_Plugin_Component {

  public $session_data = null;

  private $session_id = '';
  private $processor = null;

  public function import_site( $data ) {

    if ( ! current_user_can('manage_options') ) {
      return $this->make_error(new WP_Error( 'cornerstone', 'Unauthorized' ));
    }

    if (!isset($data['site'])) {
      return $this->make_error(new WP_Error( 'cornerstone', 'Site not specified' ));
    }

    if (!isset($data['session'])) {
      return $this->make_error(new WP_Error( 'cornerstone', 'Session not specified' ));
    }

    Networking::set_curl_timeout_begin( 30 );

    $this->session_id = 'xdi_' . substr( md5( $data['session'] ) , -16 );

    $session = get_transient( $this->session_id );

    if ( $session === false) {

      $this->session_data = array();
      $demo_data = $this->get_site_data( $data['site'] );

      if ( is_wp_error( $demo_data ) ) {
        return $this->make_error($demo_data);
      }

      $this->session_data['sliders'] = isset($data['sliders']) ? $data['sliders'] : null;
      $this->session_data['demo'] = $demo_data['jobs'];
      $this->session_data['namespace'] = $demo_data['namespace'];
      $this->save();

    } else {
      $this->session_data = $session;
    }

    $this->processor = new Cornerstone_Site_Import_Processor;
    $this->processor->setup( $this );

    // Output buffer start
    // Get next response insde
    ob_start();

    $response = $this->nextResponse();

    ob_end_clean();

    if ( is_wp_error( $response ) ) {
      return $this->make_error($response);
    }

    Networking::set_curl_timeout_end();

    if (WP_DEBUG) {
      $response['debug'] = $this->session_data;
    }

		return array(
      'success' => true,
      'data' => $response
    );

  }


  /**
   * Make a remote request to get demo data from a URL
   * @param  string $demo_url URL to demo content JSON
   * @return array           Returns demo data as an array, or WP_Error on failure
   */
  public function get_site_data( $demo_url ) {

    $request = wp_remote_get( $demo_url );

    if ( is_wp_error( $request ) ) {
      return $request;
    }

    $data = json_decode( $request['body'], true );

    if ( !is_array( $data ) ) {
      return new WP_Error( 'cornerstone', 'Failed to download demo content from remote location.' );
    }

    if ( !isset( $data['namespace'] ) ) {
      return new WP_Error( 'cornerstone', 'Demo data missing namespace' );
    }

    if ( !isset( $data['version'] ) ) {
      return new WP_Error( 'cornerstone', 'Demo data missing version number.' );
    }

    if ( !isset( $data['jobs'] ) || !is_array( $data['jobs'] ) ) {
      return new WP_Error( 'cornerstone', 'Demo data missing job list.' );
    }

    foreach ( $data['jobs'] as $job ) {
      if ( !isset( $job['task'] ) || !isset( $job['data'] ) ) {
        return new WP_Error( 'cornerstone', 'Demo data job list is not formatted correctly.' );
      }
    }

    return $data;

  }

  public function make_error($error) {
    return array(
      'success' => false,
      'data' => array(
        'error' => true,
        'message' => csi18n('app.design-cloud.site-import.failure'),
        'debug_message' => $error->get_error_message(),
        'debug' => $this->session_data
      )
    );
  }

  public function set( $key, $value ) {
    $this->session_data[$key] = $value;
  }

  public function get( $key ) {
    return (isset($this->session_data[$key])) ? $this->session_data[$key] : null;
  }
  /**
   * Save our session to a transient
   * @return bool
   */
  public function save() {
    return set_transient( $this->session_id, $this->session_data , 2 * MINUTE_IN_SECONDS );
  }

  /**
   * Delete the transient associated with this session
   * @return void
   */
  public function delete() {
    delete_transient( $this->session_id );
  }


  public function nextResponse() {

    $job = $this->processor->nextJob();

    if ( is_wp_error( $job ) ) {
      return $job;
    }

    $response = array(
      'completion' => $this->processor->completion()
    );

    if( $response['completion'] === true ) {
      $this->delete();
    } else {
      $debugMessage = $this->processor->debugMessage();
      if ($debugMessage) {
        $response['debug_message'] = $debugMessage;
      }
      $response['message'] = $this->processor->message();
      $this->save();
    }

    return $response;

  }
}
