<?php
/**
  * Ahead WebSoft Technologies Pvt Ltd.
  * @copyright  Copyright 2006-2026 Ahead WebSoft Technologies Pvt Ltd.
  * @license    https://www.socialengine.com/policies
*/

class Core_Api_Savecollection extends Core_Api_Abstract
{

  /**
   * Check if the Save to Collection feature is enabled globally.
   *
   * @return bool
   */
  public function isEnabled()
  {
    return (bool) Engine_Api::_()->getApi('settings', 'core')
      ->getSetting('savecollection.enabled', 1);
  }

  /**
   * Save a content item for the viewer.
   * Idempotent: returns existing record if already saved.
   *
   * @param User_Model_User $viewer
   * @param string $contentType
   * @param int $contentId
   * @return Core_Model_Saveditem
   * @throws Core_Model_Exception on invalid content or permission denied
   */
  public function saveItem($viewer, $contentType, $contentId)
  {
    // Validate content_type is supported
    // if (!in_array($contentType, $this->getSupportedContentTypes())) {
    //   throw new Core_Model_Exception('This content type cannot be saved', 'invalid_content_type');
    // }

    // Validate content_id is a positive integer
    $contentId = (int) $contentId;
    if ($contentId <= 0) {
      throw new Core_Model_Exception('Invalid content identifier', 'invalid_content_id');
    }

    // Validate content exists
    $item = Engine_Api::_()->getItem($contentType, $contentId);
    if (!$item || !$item->getIdentity()) {
      throw new Core_Model_Exception("The content you're looking for doesn't exist", 'content_not_found');
    }

    // Validate viewer has view permission on the content
    // $authorization = Engine_Api::_()->authorization();
    // if ($authorization && method_exists($authorization, 'isAllowed')) {
    //   if (!$authorization->isAllowed($item, $viewer, 'view')) {
    //     throw new Core_Model_Exception("The content you're looking for doesn't exist", 'content_not_found');
    //   }
    // }

    // Check if already saved (idempotent)
    $saveditemsTable = Engine_Api::_()->getDbtable('saveditems', 'core');
    $existing = $saveditemsTable->getSavedItem($viewer->getIdentity(), $contentType, $contentId);
    if ($existing) {
      return $existing;
    }

    // Create new saved item in a transaction
    $db = Engine_Db_Table::getDefaultAdapter();
    $db->beginTransaction();
    try {
      $row = $saveditemsTable->createRow();
      $row->user_id = $viewer->getIdentity();
      $row->content_type = $contentType;
      $row->content_id = $contentId;
      $row->created_at = date('Y-m-d H:i:s');
      $row->save();

      $db->commit();
    } catch (Exception $e) {
      $db->rollBack();
      throw $e;
    }

    return $row;
  }

  /**
   * Unsave a content item. Removes all collection mappings in a transaction.
   * Idempotent: returns true even if item was not saved.
   *
   * @param User_Model_User $viewer
   * @param string $contentType
   * @param int $contentId
   * @return bool
   */
  public function unsaveItem($viewer, $contentType, $contentId)
  {
    $contentId = (int) $contentId;

    // Find the saved item for this viewer
    $saveditemsTable = Engine_Api::_()->getDbtable('saveditems', 'core');
    $savedItem = $saveditemsTable->getSavedItem($viewer->getIdentity(), $contentType, $contentId);

    // If not found, already unsaved - idempotent
    if (!$savedItem) {
      return true;
    }

    $saveditemId = $savedItem->getIdentity();

    // Delete in transaction: decrement usage_count, delete mappings, then saved item
    $db = Engine_Db_Table::getDefaultAdapter();
    $db->beginTransaction();
    try {
      $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');
      $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');

      // Get all collection IDs this item belongs to and decrement their usage_count
      $collectionIds = $collectionitemsTable->getCollectionIdsForItem($saveditemId);
      foreach ($collectionIds as $collectionId) {
        $collection = $collectionsTable->find($collectionId)->current();
        if ($collection) {
          $collection->usage_count = max(0, $collection->usage_count - 1);
          $collection->updated_at = date('Y-m-d H:i:s');
          $collection->save();
        }
      }

      // Delete all collection mappings for this saved item
      $collectionitemsTable->deleteBySavedItem($saveditemId);

      // Delete the saved item row
      $savedItem->delete();

      $db->commit();
    } catch (Exception $e) {
      $db->rollBack();
      throw $e;
    }

    return true;
  }

  /**
   * Check if a content item is saved by viewer.
   *
   * @param User_Model_User $viewer
   * @param string $contentType
   * @param int $contentId
   * @return Core_Model_Saveditem|null
   */
  public function getSavedItem($viewer, $contentType, $contentId)
  {
    return Engine_Api::_()->getDbtable('saveditems', 'core')
      ->getSavedItem($viewer->getIdentity(), $contentType, (int) $contentId);
  }

  /**
   * Get paginated list of viewer's saved items.
   *
   * @param User_Model_User $viewer
   * @param array $params [page, limit, search, sort, content_type]
   * @return Zend_Paginator
   */
  public function getSavedItems($viewer, array $params = array())
  {
    $limit = isset($params['limit']) ? $params['limit'] : null;
    $limit = $this->clampPageSize($limit);
    $page = isset($params['page']) ? max(1, (int) $params['page']) : 1;

    $saveditemsTable = Engine_Api::_()->getDbtable('saveditems', 'core');
    $select = $saveditemsTable->getSavedItemsSelect($viewer->getIdentity(), $params);

    $paginator = Zend_Paginator::factory($select);
    $paginator->setItemCountPerPage($limit);
    $paginator->setCurrentPageNumber($page);

    return $paginator;
  }

  // =========================================================================
  // Collection Management Methods (Task 3.2)
  // =========================================================================

  /**
   * Create a new collection for the viewer.
   *
   * @param User_Model_User $viewer
   * @param string $name
   * @return Core_Model_Savecollection
   * @throws Core_Model_Exception on duplicate name or validation failure
   */
  public function createCollection($viewer, $name)
  {
    // Validate and sanitize the name
    $name = $this->sanitizeCollectionName($name);

		$translate = Zend_Registry::get('Zend_Translate');

    // Check for case-insensitive duplicate
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');
    $existing = $collectionsTable->getByName($viewer->getIdentity(), $name);
    if ($existing) {
      throw new Core_Model_Exception($translate->translate('A collection with this name already exists'), 'name_duplicate');
    }

    // Create the collection record
    $row = $collectionsTable->createRow();
    $row->user_id = $viewer->getIdentity();
    $row->name = $name;
    $row->usage_count = 0;
    $row->created_at = date('Y-m-d H:i:s');
    $row->updated_at = date('Y-m-d H:i:s');
    $row->save();

    return $row;
  }

  /**
   * Rename an existing collection.
   *
   * @param Core_Model_Savecollection $collection
   * @param string $newName
   * @return Core_Model_Savecollection
   */
  public function renameCollection($collection, $newName)
  {
    // Validate and sanitize the new name
    $newName = $this->sanitizeCollectionName($newName);

		$translate = Zend_Registry::get('Zend_Translate');

    // Check for case-insensitive duplicate (exclude current collection)
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');
    $existing = $collectionsTable->getByName($collection->user_id, $newName);
    if ($existing && $existing->getIdentity() != $collection->getIdentity()) {
      throw new Core_Model_Exception($translate->translate('A collection with this name already exists'), 'name_duplicate');
    }

    // Update name and updated_at
    $collection->name = $newName;
    $collection->updated_at = date('Y-m-d H:i:s');
    $collection->save();

    return $collection;
  }

  /**
   * Delete a collection and all its mappings. Saved items remain intact.
   *
   * @param Core_Model_Savecollection $collection
   * @return bool
   */
  public function deleteCollection($collection)
  {
    $db = Engine_Db_Table::getDefaultAdapter();
    $db->beginTransaction();
    try {
      // Delete all collection item mappings for this collection
      Engine_Api::_()->getDbtable('collectionitems', 'core')
        ->deleteByCollection($collection->getIdentity());

      // Delete the collection itself
      $collection->delete();

      $db->commit();
    } catch (Exception $e) {
      $db->rollBack();
      throw $e;
    }

    return true;
  }

  /**
   * Get paginated list of viewer's collections.
   *
   * @param User_Model_User $viewer
   * @param array $params [page, limit, search]
   * @return Zend_Paginator
   */
  public function getCollections($viewer, array $params = array())
  {
    $limit = isset($params['limit']) ? $params['limit'] : null;
    $limit = $this->clampPageSize($limit);
    $page = isset($params['page']) ? max(1, (int) $params['page']) : 1;

    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');
    $select = $collectionsTable->getCollectionsSelect($viewer->getIdentity(), $params);

    $paginator = Zend_Paginator::factory($select);
    $paginator->setItemCountPerPage($limit);
    $paginator->setCurrentPageNumber($page);

    return $paginator;
  }

  /**
   * Get collections for the add-to-collection sheet (max 10, ordered by usage).
   *
   * @param User_Model_User $viewer
   * @return array of Core_Model_Savecollection
   */
  public function getCollectionsForSheet($viewer)
  {
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');
    $rowset = $collectionsTable->getByUsageOrder($viewer->getIdentity(), 10);

    return $rowset->toArray();
  }

  // =========================================================================
  // Collection Item Mapping Methods (Task 3.3)
  // =========================================================================

  /**
   * Add a saved item to multiple collections.
   *
   * @param Core_Model_Saveditem $savedItem
   * @param array $collectionIds
   * @return int number of mappings created
   * @throws Core_Model_Exception if max 50 exceeded
   */
  public function addToCollections($savedItem, array $collectionIds)
  {
    $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');

    // Check max-50 constraint
    $currentCount = $collectionitemsTable->countBySavedItem($savedItem->getIdentity());
    if ($currentCount + count($collectionIds) > 50) {
      throw new Core_Model_Exception('This item cannot be added to more than 50 collections', 'max_collections_reached');
    }

    $db = Engine_Db_Table::getDefaultAdapter();
    $db->beginTransaction();
    try {
      $addedCount = 0;

      foreach ($collectionIds as $collectionId) {
        $collectionId = (int) $collectionId;

        // Validate the collection exists and belongs to the same user as the saved item
        $collection = $collectionsTable->find($collectionId)->current();
        if (!$collection || $collection->user_id != $savedItem->user_id) {
          continue;
        }

        // Check if mapping already exists (idempotent - skip if so)
        $existingMapping = $collectionitemsTable->getMapping($savedItem->getIdentity(), $collectionId);
        if ($existingMapping) {
          continue;
        }

        // Create mapping row
        $mappingRow = $collectionitemsTable->createRow();
        $mappingRow->collection_id = $collectionId;
        $mappingRow->saveditem_id = $savedItem->getIdentity();
        $mappingRow->user_id = $savedItem->user_id;
        $mappingRow->created_at = date('Y-m-d H:i:s');
        $mappingRow->save();

        // Increment the collection's usage_count and update its updated_at
        $collection->usage_count = $collection->usage_count + 1;
        $collection->updated_at = date('Y-m-d H:i:s');
        $collection->save();

        $addedCount++;
      }

      $db->commit();
    } catch (Exception $e) {
      $db->rollBack();
      throw $e;
    }

    return $addedCount;
  }

  /**
   * Remove a saved item from a specific collection.
   *
   * @param int $savedItemId
   * @param int $collectionId
   * @return bool
   */
  public function removeFromCollection($savedItemId, $collectionId)
  {
    $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');

    // Find the mapping
    $mapping = $collectionitemsTable->getMapping($savedItemId, $collectionId);

    // If not found, return true (idempotent)
    if (!$mapping) {
      return true;
    }

    // Delete the mapping row
    $mapping->delete();

    // Decrement the collection's usage_count (don't go below 0) and update updated_at
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');
    $collection = $collectionsTable->find($collectionId)->current();
    if ($collection) {
      $collection->usage_count = max(0, $collection->usage_count - 1);
      $collection->updated_at = date('Y-m-d H:i:s');
      $collection->save();
    }

    return true;
  }

  /**
   * Get paginated items within a collection.
   *
   * @param Core_Model_Savecollection $collection
   * @param array $params [page, limit, search, sort, content_type]
   * @return Zend_Paginator
   */
  public function getCollectionItems($collection, array $params = array())
  {
    $limit = isset($params['limit']) ? $params['limit'] : null;
    $limit = $this->clampPageSize($limit);
    $page = isset($params['page']) ? max(1, (int) $params['page']) : 1;

    $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');
    $select = $collectionitemsTable->getItemsSelect($collection->getIdentity(), $params);

    $paginator = Zend_Paginator::factory($select);
    $paginator->setItemCountPerPage($limit);
    $paginator->setCurrentPageNumber($page);

    return $paginator;
  }

  /**
   * Get collections that contain a specific saved item.
   *
   * @param Core_Model_Saveditem $savedItem
   * @return array
   */
  public function getCollectionsForItem($savedItem)
  {
    $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');

    // Get collection IDs for this saved item
    $ids = $collectionitemsTable->getCollectionIdsForItem($savedItem->getIdentity());

    if (empty($ids)) {
      return array();
    }

    // Fetch collection rows for those IDs
    $rowset = $collectionsTable->find($ids);

    return $rowset->toArray();
  }

  // =========================================================================
  // Lifecycle Hook Methods (Task 3.4)
  // =========================================================================

  /**
   * Remove all save data for a content item (called on content deletion).
   *
   * @param string $contentType
   * @param int $contentId
   * @return void
   */
  public function onContentDelete($contentType, $contentId)
  {
    $contentId = (int) $contentId;

    $saveditemsTable = Engine_Api::_()->getDbtable('saveditems', 'core');
    $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');

    // Find all saveditem_ids matching this content
    $select = $saveditemsTable->select()
      ->from($saveditemsTable->info('name'), array('saveditem_id'))
      ->where('content_type = ?', $contentType)
      ->where('content_id = ?', $contentId);
    $saveditemIds = $select->query()->fetchAll(Zend_Db::FETCH_COLUMN);

    // Nothing to do if no saved items reference this content
    if (empty($saveditemIds)) {
      return;
    }

    $db = Engine_Db_Table::getDefaultAdapter();
    $db->beginTransaction();
    try {
      // Delete all collection mappings for each saved item
      foreach ($saveditemIds as $saveditemId) {
        $collectionitemsTable->deleteBySavedItem($saveditemId);
      }

      // Delete all saved items matching this content
      $saveditemsTable->deleteByContent($contentType, $contentId);

      $db->commit();
    } catch (Exception $e) {
      $db->rollBack();
      throw $e;
    }
  }

  /**
   * Remove all save data for a user (called on user deletion).
   *
   * @param int $userId
   * @return void
   */
  public function onUserDelete($userId)
  {
    $userId = (int) $userId;

    $collectionitemsTable = Engine_Api::_()->getDbtable('collectionitems', 'core');
    $collectionsTable = Engine_Api::_()->getDbtable('collections', 'core');
    $saveditemsTable = Engine_Api::_()->getDbtable('saveditems', 'core');

    $db = Engine_Db_Table::getDefaultAdapter();
    $db->beginTransaction();
    try {
      // Delete all collection item mappings for this user first
      $collectionitemsTable->deleteByUser($userId);

      // Delete all collections owned by this user
      $collectionsTable->deleteByUser($userId);

      // Delete all saved items for this user
      $saveditemsTable->deleteByUser($userId);

      $db->commit();
    } catch (Exception $e) {
      $db->rollBack();
      throw $e;
    }
  }

  // =========================================================================
  // Helper Methods
  // =========================================================================

  /**
   * Clamp page size to allowed range.
   * Reads min/max/default from settings.
   *
   * @param int|null $limit
   * @return int
   */
  public function clampPageSize($limit)
  {
    $settings = Engine_Api::_()->getApi('settings', 'core');
    $default = (int) $settings->getSetting('savecollection.pagination.default', 20);
    $min = (int) $settings->getSetting('savecollection.pagination.min', 10);
    $max = (int) $settings->getSetting('savecollection.pagination.max', 50);

    if ($limit === null || !is_numeric($limit) || (int) $limit <= 0) {
      return $default;
    }

    $limit = (int) $limit;

    if ($limit < $min) {
      return $min;
    }
    if ($limit > $max) {
      return $max;
    }

    return $limit;
  }

  /**
   * Get supported content types for saving.
   *
   * @return array
   */
  public function getSupportedContentTypes()
  {
    return array(
      
    );
  }

  /**
   * Validate and sanitize a collection name.
   *
   * @param string $name
   * @return string sanitized name
   * @throws Core_Model_Exception on validation failure
   */
  public function sanitizeCollectionName($name)
  {
    // Trim whitespace
    $name = trim($name);

    // Reject empty/whitespace-only
    if ($name === '' || strlen($name) === 0) {
      throw new Core_Model_Exception('Collection name is required', 'name_required');
    }

    // Strip HTML tags
    $name = strip_tags($name);

    // Re-check after stripping tags (could be empty if input was only HTML)
    $trimmed = trim($name);
    if ($trimmed === '') {
      throw new Core_Model_Exception('Collection name is required', 'name_required');
    }
    $name = $trimmed;

    // Reject < 5 chars
    if (mb_strlen($name, 'UTF-8') < 5) {
      throw new Core_Model_Exception('Collection name must be at least 5 characters', 'name_too_short');
    }

    // Reject > 50 chars
    if (mb_strlen($name, 'UTF-8') > 50) {
      throw new Core_Model_Exception('Collection name must be 50 characters or less', 'name_too_long');
    }

    return $name;
  }

  /**
   * Get display info (title, photo, href) for a saved item.
   * For activity posts, falls back to first attachment's title/photo.
   *
   * @param Core_Model_Item_Abstract $contentItem
   * @return array [title, photo, href]
   */
  public function getContentDisplayInfo($contentItem)
  {
    $title = '';
    $photo = '';
    $href = 'javascript:void(0);';

    if (!$contentItem || !$contentItem->getIdentity()) {
      return array('title' => $title, 'photo' => $photo, 'href' => $href);
    }

    // Get href
    if (method_exists($contentItem, 'getHref')) {
      $href = $contentItem->getHref();
    }

    // Try to get title directly
    if (method_exists($contentItem, 'getTitle') && $contentItem->getTitle()) {
      $title = $contentItem->getTitle();
    }

    // Try to get photo directly
    if (method_exists($contentItem, 'getPhotoUrl') && $contentItem->getPhotoUrl()) {
      $photo = $contentItem->getPhotoUrl();
    }

    // For activity_action: fallback to attachment if no title/photo
    if ($contentItem->getType() == 'activity_action' && (empty($title) || empty($photo))) {
      try {
        $attachments = $contentItem->getAttachments();
        if (!empty($attachments)) {
          $firstAttachment = $attachments[0]->item;
          if ($firstAttachment) {
            if (empty($title) && method_exists($firstAttachment, 'getTitle') && $firstAttachment->getTitle()) {
              $title = $firstAttachment->getTitle();
            }
            if (empty($photo) && method_exists($firstAttachment, 'getPhotoUrl') && $firstAttachment->getPhotoUrl()) {
              $photo = $firstAttachment->getPhotoUrl();
            }
          }
        }
        // If still no title, use the post body/description as fallback
        if (empty($title) && method_exists($contentItem, 'getDescription') && $contentItem->getDescription()) {
          $body = strip_tags($contentItem->getDescription());
          $title = mb_strlen($body, 'UTF-8') > 60 ? mb_substr($body, 0, 60, 'UTF-8') . '...' : $body;
        }
      } catch (Exception $e) {}
    }

    // Final fallback
    if (empty($title)) {
      $title = ucfirst(str_replace('_', ' ', $contentItem->getType()));
    }

    return array('title' => $title, 'photo' => $photo, 'href' => $href);
  }
}
