<?php

namespace ProcessWire;

/**
 * PWCommerce: FieldtypePWCommerceDownloadSettings
 *
 * Fieldtype that stores and outputs values of a PWCommerce download settings.
 *
 * @author Francis Otieno (Kongondo) <kongondo@gmail.com> kongondo.com
 *
 *
 *
 * FieldtypePWCommerceDownloadSettings for PWCommerce
 * Copyright (C) 2025 by Francis Otieno
 * MIT License
 *
 */


class FieldtypePWCommerceDownloadSettings extends Fieldtype
{


	public static function getModuleInfo() {
		return array(
			'title' => 'PWCommerce Download Settings: Fieldtype',
			'author' => 'Francis Otieno (Kongondo)',
			'version' => "100",
			'href' => 'http://kongondo.com',
			'summary' => 'Field that stores a PWCommerce download settings.',
			'installs' => 'InputfieldPWCommerceDownloadSettings',
		);
	}

	/**
	 * Return the required Inputfield used to populate a field of this type.
	 *
	 */
	public function getInputfield(Page $page, Field $field) {
		// $inputfield = $this->modules->get("InputfieldPWCommerceDownloadSettings");
		$inputfield = $this->wire('modules')->get("InputfieldPWCommerceDownloadSettings");
		// our inputfield requires a Page and Field
		// @note: easier to set this way than using set() since we will then have to check keys
		$inputfield->setPage($page); // this is a method in InputfieldPWCommerceDownloadSettings
		$inputfield->setField($field); // this is a method in InputfieldPWCommerceDownloadSettings
		// $inputfield->set('page', $page);
		// $inputfield->set('field', $field);
		return $inputfield;
	}

	/**
	 * Return a blank ready-to-populate version of a field of this type.
	 *
	 */
	public function getBlankValue(Page $page, Field $field) {
		$record = new WireData();
		$record->setTrackChanges(true);
		return $record;
	}

	/**
	 * Set the config option fields for this Field.
	 *
	 * These appear in the 'Details' Tab when editing an instance of this Field.
	 *
	 */
	public function ___getConfigInputfields(Field $field) {
		$inputfields = parent::___getConfigInputfields($field);
		return $inputfields;
	}

	/**
	 * Format a value for output, called when a Page's outputFormatting is on.
	 *
	 */
	public function formatValue(Page $page, Field $field, $value) {
		// TODO:???
		return $value;
	}

	/**
	 * Given a value, make it clean for storage within a Page
	 * Implementation is required by Fieldtype modules, as this method is abstract.
	 * This method should remove anything that's invalid from the given value. If it can't be sanitized, it should be made blank.
	 */
	public function sanitizeValue(Page $page, Field $field, $value) {

		// if given a blank value, return a valid blank value
		if (empty($value)) {
			return $this->getBlankValue($page, $field);
		}

		// if given something other than an WireData, throw an error
		if (!$value instanceof WireData) {
			throw new WireException("Value set to field '$field->name' must be a WireData");
		}

		return $value;
	}

	private function getDownloadPageFile(Page $page) {
		$download = $page->get(PwCommerce::DOWNLOAD_FILE_FIELD_NAME);
		// TODO getting the formatted version, ok?
		// $download = $page->getFormatted(PwCommerce::DOWNLOAD_FILE_FIELD_NAME);
		return $download;
	}

	/**
	 * Given a raw value (value as stored in DB), return the value as it would appear in a Page object.
	 *
	 * @param Page $page.
	 * @param Field $field.
	 * @param string|int|array $value.
	 * @access public
	 * @return string|int|array|object $value.
	 *
	 */
	public function ___wakeupValue(Page $page, Field $field, $value) {
		// if for some reason we already got a valid value, then just return it
		if ($value instanceof WireData) {
			return $value;
		}

		// start a blank value to be populated
		$record = $this->getBlankValue($page, $field);

		// if we were given a blank value, then we've got nothing to do: just return getBlankValue
		if (empty($value) || !is_array($value)) {

			return $record;
		}

		// DOWNLOAD MAXIMUM DOWNLOADS
		// note we are converting 'data' to 'record->maximumDownloads'
		$maximumDownloads = (int) $value['data'];

		$record->maximumDownloads = $maximumDownloads;
		// aliases
		$record->maximum = $maximumDownloads;

		//--------
		// DOWNLOAD TIME TO DOWNLOAD
		$timeToDownload = $this->wire('sanitizer')->text($value['time_to_download']);
		$record->timeToDownload = $timeToDownload;
		// aliases
		$record->time = $timeToDownload;
		$record->validity = $timeToDownload;
		$record->period = $timeToDownload;


		//--------
		// RUNTIME
		$record->id = $page->id;
		$record->title = $page->title;
		$record->description = $page->pwcommerce_description;
		// download pagefile
		$record->filename = '';
		$record->name = '';
		$downloadPageFile = $this->getDownloadPageFile($page);

		if (!empty($downloadPageFile)) {
			$record->filename = $downloadPageFile->filename();
			$record->name = $downloadPageFile->basename();
			// TODO THESE TWO ARE TIMESTAMPS! GET FORMATTED?
			$record->created = $downloadPageFile->created;
			$record->modified = $downloadPageFile->modified;
			$record->filesize = $downloadPageFile->filesize;
			$record->filesizeStr = $downloadPageFile->filesizeStr;
			// TODO @note: we use the page description instead!(?)
			// $record->description = $downloadPageFile->description;
		}


		$record->setTrackChanges(true); // track changes for the record TODO? reset instead?

		return $record;
	}

	/**
	 * Given an 'awake' value, as set by wakeupValue, convert the value back to a basic type for storage in DB.
	 *
	 * @param Page $page.
	 * @param Field $field.
	 * @param string|int|array|object $value.
	 * @access public
	 * @return array $sleepValue.
	 *
	 */
	public function ___sleepValue(Page $page, Field $field, $value) {

		$sleepValue = [];

		// if we are given something other than a WireData,
		// then just return a blank array
		if (!$value instanceof WireData) {
			return $sleepValue;
		}

		// convert records to an array within sleepValue
		$sleepValue = [
			// note: record->maximumDownloads is becoming data which is a required column in a Fieldtype
			'data' => (int) $value->maximumDownloads,
			'time_to_download' => $this->wire('sanitizer')->text($value->timeToDownload)
		];

		return $sleepValue;
	}

	/**
	 * Return the database schema that defines a Download item
	 *
	 */
	public function getDatabaseSchema(Field $field) {

		$schema = parent::getDatabaseSchema($field);

		// 'data' is a required field for any Fieldtype, and we're using it to represent our 'item->maximumDownloads' field (column in db)
		// we reuse this required field/column (data) for item->value
		$schema['data'] = 'INT UNSIGNED NULL';
		$schema['time_to_download'] = 'VARCHAR(255) NULL';

		//----------------------------

		// indexes, for any fields that need to be searchable from selectors
		// make all our fields searchable
		$schema['keys']['data'] = 'KEY data (data)'; // for 'max_downloads'
		$schema['keys']['time_to_download'] = 'FULLTEXT KEY time_to_download (time_to_download)';


		return $schema;
	}

	/**
	 * Method called when the field is database-queried from a selector
	 *
	 */
	public function getMatchQuery($query, $table, $subfield, $operator, $value) {

		// match properties/subfields to model subfields/columns
		if (in_array($subfield, ['maximumDownloads', 'maximum_downloads', 'maximum'])) {
			$subfield = 'data';
		} else if (in_array($subfield, ['timeToDownload', 'time', 'validity', 'period'])) {
			$subfield = 'time_to_download';
		}
		//---------

		/*
													- MySQL knows what "=" and ">" are, but it doesn't know what "%=", "~=", "^=" OR "$=" are
													- the latter allow for finer, fulltext search
													*/
		// if 'normal' sql operators, do 'normal' query, else do fulltext search
		if ($this->wire('database')->isOperator($operator)) {
			return parent::getMatchQuery($query, $table, $subfield, $operator, $value);
		} else {
			$ft = new DatabaseQuerySelectFulltext($query);
			$ft->match($table, $subfield, $operator, $value);
			return $query;
		}
	}
}