<?php

use ModuleRelationships\Definition;

App::uses('AppModel', 'Model');

class ModuleRelationship extends AppModel {

	public $hasMany = [
		'ModuleRelationshipParameters'
	];

	/**
	 * Get the currently related modules.
	 *
	 * @param Definition $relationship The definition of the relationship.
	 * @param int        $id           The id of the module to get the definition for.
	 *
	 * @return array The found models in the relationship.
	 */
	public function getRelated(Definition $relationship, $id) {

		// Make sure to count symmetrical relationships
		$fields = $relationship->isSymmetric() ? [ 'master' => 'slave', 'slave' => 'master' ] : [ 'master' => 'slave' ];

		// Get the IDs
		$list = [];
		foreach ($fields as $master => $slave) {
			$data = $this->find('all', [
				'fields'     => [
					'id' => 'master_fk', 'slave_fk' ],
				'contain'    => [
					'ModuleRelationshipParameters.key', 'ModuleRelationshipParameters.value' ],
				'conditions' => [
					"{$this->alias}.name"             => $relationship->getName(),
					"{$this->alias}.{$master}_module" => $relationship->getMaster(),
					"{$this->alias}.{$master}_fk"     => $id,
					"{$this->alias}.{$slave}_module"  => $relationship->getSlave() ],
				'order'      => [
					'ordering' => ASC ]
			]);

			// Extract the values
			foreach ($data as $record) {

				// Get the additional parameters
				$parameters = [ 'id' => $record[$this->alias]['id'] ];
				foreach ($record['ModuleRelationshipParameters'] as $parameter) {
					$parameters[$parameter['key']] = $parameter['value'];
				}

				// Compact to list
				$list[$record[$this->alias]["{$slave}_fk"]] = $parameters;
			}
		}

		// Return the loaded data
		return $this->extractData($relationship->getSlave(), $relationship->getName(), $list);
	}

	/**
	 * Extract the data from the relation data.
	 *
	 * @param string $module      The module to extract the data from.
	 * @param string $name        The name of the relationship.
	 * @param array  $relatedData The related data from the getRelated() method.
	 *
	 * @return mixed The data from the
	 */
	private function extractData($module, $name, $relatedData) {

		// Skip if no data
		if (empty($relatedData)) {
			return [];
		}

		// Extract
		$list = array_keys($relatedData);
		$data = ClassRegistry::init($module)->find('all', [
			'sort'       => $list,
			'conditions' => [
				"{$module}.id" => $list ]
		]);

		// Assign properties
		foreach ($data as $i => $record) {
			$data[$i]['Relationship'] = $relatedData[$record[$module]['id']];
			$data[$i]['slave_fk'] = $i;
		}

		return $data;
	}

	/**
	 * Replace the existing relationship values with new ones.
	 *
	 * @param AppModel $module The name of the primary module.
	 * @param int      $name   The name of the relationship.
	 * @param array    $data   The relationship data to save.
	 */
	public function replace(AppModel $module, $name, $data) {
		$parameters = [];
		$ordering = 0;
		$this->getDataSource()->begin();

		// Get the info of the relationship
		$relationship = $module->getRelationship($name);

		// Delete previous
		$record = [
			'name'          => $relationship->getName(),
			'master_module' => $relationship->getMaster(),
			'master_fk'     => $module->id,
		];
		$this->deleteAll($record);

		// Set the new ones
		foreach ($data as $item) {

			// Get the foreign key of the slave module
			$foreignKey = $item['slave_fk'];
			unset($item['slave_fk']);

			// Save
			$record['slave_module'] = $relationship->getSlave();
			$record['slave_fk'] = $foreignKey;
			$record['ordering'] = ++$ordering;
			$this->create();
			$this->save($record);

			// Save the parameters as well
			if (!empty($item)) {
				foreach ($item as $key => $value) {
					$parameters[] = [
						'module_relationship_id' => $this->id,
						'key'                    => $key,
						'value'                  => $value
					];
				}
			}
		}

		// Save the parameters
		if (!empty($parameters)) {
			ClassRegistry::init('ModuleRelationships.ModuleRelationshipParameter')->saveAll($parameters);
		}
		$this->getDataSource()->commit();
	}

}