<?php

use ModuleRelationships\Definition;

class RelatedBehavior extends ModelBehavior {

	/**
	 * @inheritdoc
	 *
	 * @param AppModel $Model
	 */
	public function setup(Model $Model, $settings = []) {
		$Model->relationships = [];

		foreach ($settings as $name => $parameters) {
			$Model->relationships[$name] = static::parse($Model->name, $name, $parameters);
		}
	}

	/**
	 * Get the currently related modules.
	 *
	 * @param Model  $Model            The calling model.
	 * @param int    $id               The id of the module to get the definition for.
	 * @param string $relationshipName The name of the relationship.
	 *
	 * @return mixed The found models in the relationship.
	 */
	public function getRelated(Model $Model, $relationshipName, $id) {
		return ClassRegistry::init('ModuleRelationships.ModuleRelationship')->getRelated($this->getRelationship($Model, $relationshipName), $id);
	}

	/**
	 * Get the list of all relationships.
	 *
	 * @param Model $Model The calling model.
	 *
	 * @return Definition[] The list of all defined relationships.
	 */
	public function getRelationships(Model &$Model) {
		/** @var AppModel $Model */
		return $Model->relationships;
	}

	/**
	 * Get the list of all relationships.
	 *
	 * @param Model  $Model The calling model.
	 * @param string $name  The name of the relationship.
	 *
	 * @return Definition The definition of the requested relationship.
	 * @throws Exception If the requested relationship is not defined for this model.
	 */
	public function getRelationship(Model &$Model, $name) {
		/** @var AppModel $Model */

		// Make sure the relationship exists
		if (!key_exists($name, $Model->relationships)) {
			throw new \Exception("The requested relationship `{$name}` does not exist in module `$Model->name`.");
		}

		return $Model->relationships[$name];
	}

	/**
	 * Parse the parameters into a definition.
	 *
	 * @param string $master     The name of the master.
	 * @param string $name       The name of the relationship.
	 * @param array  $parameters The parameters model defined.
	 *
	 * @return Definition The definition of the relationship/
	 * @throws Exception
	 */
	private static function parse($master, $name, $parameters) {

		// Get the name
		if (!key_exists(0, $parameters)) {
			throw new \Exception("The relationship `{$name}` in module `{$master}` does not have a defined slave module.");
		}
		$slave = $parameters[0];
		unset($parameters[0]);

		// Defaults
		$symmetrical = Definition::TYPE_ASYMMETRIC;
		$options = key_exists('options', $parameters) ? $parameters['options'] : [];
		unset($parameters['options']);

		// Allowed options
		$allowed = [ 'conditions', 'filter', 'order', 'limit' ];
		foreach ($allowed as $key) {
			if (key_exists($key, $parameters)) {
				$options[$key] = $parameters[$key];
				unset($parameters[$key]);
			}
		}

		// Get values
		foreach ($parameters as $key => $value) {
			switch ($key) {
				case 'symmetrical':
					$symmetrical = (bool) $value;
					break;

				default:
					throw new \Exception("Unknown key `{$key}` in definition of relationship `{$name}` in module `{$master}`.");
			}
		}

		return new Definition($symmetrical, $master, $slave, $name, $options);
	}

	/** @inheritdoc */
	public function afterSave(Model $model, $created, $options = []) {

		// If there were relationships
		if (key_exists('Relationships', $model->data[$model->alias])) {
			$relationships = $model->data[$model->alias]['Relationships'];

			// Handle all relationships
			foreach ($relationships as $relationship) {

				// Check if there are new relationships to save
				$data = [];
				if (key_exists('Relationship', $model->data[$model->alias]) && key_exists($relationship, $model->data[$model->alias]['Relationship'])) {
					$data = $model->data[$model->alias]['Relationship'][$relationship];
				}

				// Update
				ClassRegistry::init('ModuleRelationships.ModuleRelationship')->replace($model, $relationship, $data);
			}
		}
	}

}