<?php
namespace SabaiApps\Directories\Component\Location\CSVImporter;

use SabaiApps\Directories\Component\Entity\CSVImporter\AbstractTermsCSVImporter;
use SabaiApps\Directories\Component\CSV\Importer\IWpAllImportImporter;
use SabaiApps\Directories\Component\Entity\Model\Field;
use SabaiApps\Directories\Exception;

class LocationCSVImporter extends AbstractTermsCSVImporter implements IWpAllImportImporter {

	protected static $_geocodeCount = 0;

	public function csvImporterColumns( Field $field ) {
		switch ( $this->_name ) {
			case 'location_address':
				return array(
					'address'  => __( 'Full Address', 'directories-pro' ),
					'street'   => __( 'Address Line 1', 'directories-pro' ),
					'street2'  => __( 'Address Line 2', 'directories-pro' ),
					'city'     => __( 'City', 'directories-pro' ),
					'province' => __( 'State / Province / Region', 'directories-pro' ),
					'zip'      => __( 'Postal / Zip Code', 'directories-pro' ),
					'country'  => __( 'Country', 'directories-pro' ),
					'lat'      => __( 'Latitude', 'directories-pro' ),
					'lng'      => __( 'Longitude', 'directories-pro' ),
					'zoom'     => __( 'Zoom Level', 'directories-pro' ),
					'term_id'  => __( 'Location Taxonomy Term', 'directories-pro' ),
					'timezone' => __( 'Timezone', 'directories-pro' ),
				);
		}
	}

	public function csvImporterSettingsForm( Field $field, array $settings, $column, $enclosure, array $parents = array() ) {
		switch ( $this->_name ) {
			case 'location_address':
				$form = array();
				switch ( $column ) {
					case 'term_id':
						return parent::csvImporterSettingsForm( $field, $settings, $column, $enclosure, $parents );
					case 'address':
						$form += array(
							'geocode' => array(
								'#type'          => 'checkbox',
								'#title'         => __( 'Geocode address', 'directories-pro' ),
								'#description'   => __( 'Use the Google geocoding service to resolve the latitude/longitude coordinate from the address. This will populate all other address components of the field.', 'directories-pro' ),
								'#default_value' => true,
								'#horizontal'    => true,
							),
						);
						break;
				}

				return $form + $this->_acceptMultipleValues( $field, $enclosure, $parents );
		}
	}

	public function csvImporterDoImport( Field $field, array $settings, $column, $value, &$formStorage, array &$logs ) {
		switch ( $this->_name ) {
			case 'location_address':
				if ( $column === 'term_id' ) {
					return parent::csvImporterDoImport( $field, $settings, $column, $value, $formStorage, $logs );
				}

				if ( ! empty( $settings['_multiple'] ) ) {
					if ( ! $values = explode( $settings['_separator'], $value ) ) {
						return;
					}
				} else {
					$values = array( $value );
				}
				if ( ! $values = array_filter(
					$values,
					function ( $value ) {
						return strlen( trim( $value ) ) > 0; }
				) ) {
					return;
				}
				$ret = array();
				switch ( $column ) {
					case 'address':
						foreach ( $values as $value ) {
							$value = array(
								'address' => $value,
							);
							if ( $settings['geocode'] ) {
								try {
									$geocode_result = $this->_application->Location_Api_geocode( $value['address'], false );
									$value         += array(
										'street'   => $geocode_result['street'],
										'city'     => $geocode_result['city'],
										'province' => $geocode_result['province'],
										'zip'      => $geocode_result['zip'],
										'country'  => $geocode_result['country'],
										'lat'      => $geocode_result['lat'],
										'lng'      => $geocode_result['lng'],
									);
								} catch ( Exception\IException $e ) {
									$logs['error'][] = $e->getMessage();
								}
								if ( ! empty( $value['lat'] )
									&& ! empty( $value['lng'] )
								) {
									try {
										$value['timezone'] = $this->_application->Location_Api_timezone( array( $value['lat'], $value['lng'] ) );
									} catch ( Exception\IException $e ) {
										$logs['error'][] = $e->getMessage();
									}
								}
								++self::$_geocodeCount;
								if ( self::$_geocodeCount % 10 === 0 ) {
									sleep( 1 ); // this is to prevent rate limit of 10 requests per second
								}
							}
							$ret[] = $value;
						}
						break;
					default:
						foreach ( $values as $value ) {
							$ret[] = array( $column => $value );
						}
				}

				return $ret;
		}
	}

	protected function _getValueForImportTerm( $termId ) {
		return array( 'term_id' => $termId );
	}

	protected function _getTaxonomyBundle( Field $field ) {
		if ( empty( $field->Bundle->info['taxonomies']['location_location'] ) ) {
			return;
		}

		$bundle_name = $field->Bundle->info['taxonomies']['location_location'];
		return $this->_application->Entity_Bundle( $bundle_name );
	}

	public function csvWpAllImportImporterAddField( \RapidAddon $addon, Field $field ) {
		switch ( $this->_name ) {
			case 'location_address':
				$search_by_suffix = '';
				if ( ! $geocoding_api_enabled = $this->_application->Location_Api( 'Geocoding' ) ) {
					$search_by_suffix = ' - ' . __( 'WARNING! No geocoding provider selected in Settings -> Map.', 'directories-pro' );
				}
				$addon->add_title( $field->getFieldLabel() );
				$addon->add_field(
					$field->getFieldName() . '-type',
					'',
					'radio',
					array(
						'address' => array(
							__( 'Search by address', 'directories-pro' ) . $search_by_suffix,
							$addon->add_field(
								$field->getFieldName() . '-search-address',
								'',
								'text'
							),
						),
						'latlng'  => array(
							__( 'Search by coordinates', 'directories-pro' ) . $search_by_suffix,
							$addon->add_field(
								$field->getFieldName() . '-search-lat',
								__( 'Latitude', 'directories-pro' ),
								'text',
								null,
								'Example: 34.0194543'
							),
							$addon->add_field(
								$field->getFieldName() . '-search-lng',
								__( 'Longitude', 'directories-pro' ),
								'text',
								null,
								'Example: -118.4911912'
							),
						),
						'manual'  => array(
							__( 'Enter manually', 'directories-pro' ),
							$addon->add_field(
								$field->getFieldName() . '-address',
								__( 'Full Address', 'directories-pro' ),
								'text'
							),
							$addon->add_field(
								$field->getFieldName() . '-street',
								__( 'Address Line 1', 'directories-pro' ),
								'text'
							),
							$addon->add_field(
								$field->getFieldName() . '-street2',
								__( 'Address Line 2', 'directories-pro' ),
								'text'
							),
							$addon->add_field(
								$field->getFieldName() . '-city',
								__( 'City', 'directories-pro' ),
								'text'
							),
							$addon->add_field(
								$field->getFieldName() . '-province',
								__( 'State / Province / Region', 'directories-pro' ),
								'text'
							),
							$addon->add_field(
								$field->getFieldName() . '-zip',
								__( 'Postal / Zip Code', 'directories-pro' ),
								'text'
							),
							$addon->add_field(
								$field->getFieldName() . '-country',
								__( 'Country', 'directories-pro' ),
								'text',
								null,
								'Example: US'
							),
							$addon->add_field(
								$field->getFieldName() . '-lat',
								__( 'Latitude', 'directories-pro' ),
								'text',
								null,
								'Example: 34.0194543'
							),
							$addon->add_field(
								$field->getFieldName() . '-lng',
								__( 'Longitude', 'directories-pro' ),
								'text',
								null,
								'Example: -118.4911912'
							),
							$addon->add_field(
								$field->getFieldName() . '-timezone',
								__( 'Timezone', 'directories-pro' ),
								'text',
								null,
								'Example: America/New_York'
							),
						),
					),
					'',
					false,
					$geocoding_api_enabled ? 'address' : 'manual'
				);
				return true;
		}
	}

	public function csvWpAllImportImporterDoImport( \RapidAddon $addon, Field $field, array $data, $options, array $article ) {
		switch ( $this->_name ) {
			case 'location_address':
				if ( ! isset( $data[ $field->getFieldName() . '-type' ] ) ) {
					return;
				}

				if ( ! $field->isCustomField() ) {
					$addon->_save_post_callbacks[] = array( array( $this, 'savePostCallback' ), array( $field->getFieldName() ) );
				}
				switch ( $type = $data[ $field->getFieldName() . '-type' ] ) {
					case 'address':
					case 'latlng':
						if ( $type === 'address' ) {
							$addon->log( '[drts] Importing by address search ...' );
							if ( ! isset( $data[ $field->getFieldName() . '-search-address' ] )
								|| ( ! $address = trim( $data[ $field->getFieldName() . '-search-address' ] ) )
							) {
								return;
							}

							$addon->log( '[drts] Geocoding address `' . $address . '` ...' );
							try {
								$result = $this->_application->Location_Api_geocode( $address );
							} catch ( \Exception $e ) {
								$addon->log( '[drts] Error geocoding address: ' . $e->getMessage() );
								return;
							}
							$addon->log( '[drts] Geocoding results: ' . json_encode( $result ) );
						} else {
							$addon->log( '[drts] Importing by lat/lng search' );
							if ( ! isset( $data[ $field->getFieldName() . '-search-lat' ] )
								|| ! isset( $data[ $field->getFieldName() . '-search-lng' ] )
								|| ( ! $lat = trim( $data[ $field->getFieldName() . '-search-lat' ] ) )
								|| ( ! $lng = trim( $data[ $field->getFieldName() . '-search-lng' ] ) )
							) {
								return;
							}

							$addon->log( '[drts] Reverse geocoding coordinates `' . $lat . ',' . $lng . '` ...' );
							try {
								$result = $this->_application->Location_Api_reverseGeocode( array( $lat, $lng ) );
							} catch ( \Exception $e ) {
								$addon->log( '[drts] Error reverse geocoding lat/lng: ' . $e->getMessage() );
								return;
							}
							$addon->log( '[drts] Reverse geocoding results: ' . json_encode( $result ) );
						}

						$value = array();
						foreach ( array( 'address', 'street', 'city', 'province', 'zip', 'country', 'lat', 'lng' ) as $key ) {
							$value[ $key ] = isset( $result[ $key ] ) ? $result[ $key ] : '';
						}
						if ( ! empty( $value['lat'] )
							&& ! empty( $value['lng'] )
						) {
							try {
								$value['timezone'] = $this->_application->Location_Api_timezone( array( $value['lat'], $value['lng'] ) );
							} catch ( \Exception $e ) {
								$addon->log( '[drts] Error fetching timezone: ' . $e->getMessage() );
								return;
							}
						}

						return empty( $value ) ? null : array( $value );

					default:
						$addon->log( '[drts] Importing by manual address input' );
						$value = array();
						foreach ( array( 'address', 'street', 'street2', 'city', 'province', 'zip', 'country', 'timezone' ) as $key ) {
							$value[ $key ] = $data[ $field->getFieldName() . '-' . $key ];
						}
						foreach ( array( 'lat', 'lng' ) as $key ) {
							if ( empty( $data[ $field->getFieldName() . '-' . $key ] )
								|| ! is_numeric( $data[ $field->getFieldName() . '-' . $key ] )
							) {
								continue;
							}

							$value[ $key ] = $data[ $field->getFieldName() . '-' . $key ];
						}
						return array( $value );
				}
		}
	}

	public function savePostCallback( $entity, &$values, $fieldName ) {
		if ( ! empty( $values['location_location'] )
			&& ( $field_value = $entity->getSingleFieldValue( $fieldName ) )
		) {
			$location_term_ids      = array_values( $values['location_location'] );
			$field_value['term_id'] = $location_term_ids[ count( $location_term_ids ) - 1 ];
			$values[ $fieldName ]   = $field_value;
		}
	}
}
