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

use SabaiApps\Directories\Component\System\Tool\AbstractTool;
use SabaiApps\Directories\Exception;

class LoadGeoDataSystemTool extends AbstractTool
{
    protected function _systemToolInfo()
    {
        return [
            'label' => __('Load geolocation data', 'directories-pro'),
            'description' => __('This tool will load geolocation data (lat/lng/timezone) for the default location address field of each content item if currently empty.', 'directories-pro'),
            'weight' => 80,
        ];
    }

    public function systemToolInit(array $settings, array &$storage, array &$logs)
    {
        $ret = [];
        $langs = $this->_application->getPlatform()->getLanguages();
        foreach ($this->_application->Entity_Bundles() as $bundle) {
            if (!empty($bundle->info['is_taxonomy'])
                || (!$field = $this->_application->Entity_Field($bundle, $field_name = 'location_address'))
            ) continue;

            if (empty($langs)) {
                $ret[$bundle->name] = $this->_count($bundle, $field_name);
            } else {
                foreach ($langs as $lang) {
                    $ret[$bundle->name . '-' . $lang] = $this->_count($bundle, $field_name, $lang);
                }
            }
        }
        return $ret;
    }

    protected function _count($bundle, $fieldName, $lang = null)
    {
        return $this->_application->Entity_Query($bundle->entitytype_name)
            ->fieldIs('bundle_name', $bundle->name)
            ->fieldIsNotNull($fieldName, 'address')
            ->count($lang);
    }

    public function systemToolRunTask($task, array $settings, $iteration, $total, array &$storage, array &$logs)
    {
        if (strpos($task, '-')) {
            $task_parts = explode('-', $task);
            if (!$bundle = $this->_application->Entity_Bundle($task_parts[0])) return false;

            $lang = $task_parts[1];
        } else {
            if (!$bundle = $this->_application->Entity_Bundle($task)) return false;

            $lang = null;
        }
        if (!$field = $this->_application->Entity_Field($bundle, 'location_address')) return false;

        $paginator = $this->_application->Entity_Query($bundle->entitytype_name)
            ->fieldIs('bundle_name', $bundle->name)
            ->fieldIsNotNull($field->getFieldName(), 'address')
            ->sortById()
            ->paginate(50, 0, $lang)
            ->setCurrentPage($iteration);
        foreach ($paginator->getElements() as $entity) {
            $values = $entity->getFieldValue($field->getFieldName());
            $save = false;
            $terms = [];
            if ($_terms = $entity->getFieldValue('location_location')) {
                foreach ($_terms as $term) {
                    $terms[$term->getId()] = $term->getTitle();
                }
            }
            foreach (array_keys($values) as $i) {
                if (empty($values[$i]['lat']) || empty($values[$i]['lng'])) {
                    $query = []; // string passed to geocoding API

                    if (isset($values[$i]['address'])) {
                        // Use full address
                        $query[] = $values[$i]['address'];
                    } else {
                        foreach (['street', 'street2', 'city', 'province', 'zip', 'country'] as $input_key) {
                            if (isset($values[$i][$input_key])) {
                                $query[] = $values[$i][$input_key];
                            }
                        }
                        // Append location term titles
                        if (!empty($values[$i]['term_id'])
                            && isset($terms[$values[$i]['term_id']])
                        ) {
                            $term = $terms[$values[$i]['term_id']];
                            $query[] = $term->getTitle();
                            foreach (array_reverse($term->getCustomProperty('parent_titles')) as $term_title) {
                                $query[] = $term_title;
                            }
                        }
                    }

                    $query = trim(implode(' ', $query));
                    if (!strlen($query)) {
                        $logs['warning'][] = sprintf(
                            'Skipped fetching geolocation data for %s (ID: %d, Error: No address data to geocode)',
                            $entity->getTitle(),
                            $entity->getId()
                        );
                        continue;
                    } // skip since nothing to query

                    try {
                        $result = $this->_application->Location_Api_geocode($query, false);
                        $values[$i]['lat'] = $result['lat'];
                        $values[$i]['lng'] = $result['lng'];
                        $save = true;
                    } catch (Exception\IException $e) {
                        $title = $entity->getTitle();
                        if (!strlen($title)) $title = __('(no title)', 'directories-pro');
                        $logs['error'][] = sprintf(
                            'Failed fetching geolocation data for %s (ID: %d, Error: %s)',
                            $title,
                            $entity->getId(),
                            $e->getMessage()
                        );
                        continue;
                    }
                }
                if (empty($values[$i]['timezone'])) {
                    try {
                        $values[$i]['timezone'] = $this->_application->Location_Api_timezone([$values[$i]['lat'], $values[$i]['lng']]);
                        $save = true;
                    } catch (Exception\IException $e) {
                        $title = $entity->getTitle();
                        if (!strlen($title)) $title = __('(no title)', 'directories-pro');
                        $logs['error'][] = sprintf(
                            'Failed fetching timezone for %s (ID: %d, Error: %s)',
                            $title,
                            $entity->getId(),
                            $e->getMessage()
                        );
                        continue;
                    }
                }
            }
            if ($save) {
                try {
                    $this->_application->Entity_Save($entity, [$field->getFieldName() => $values]);
                } catch (Exception\IException $e) {
                    $logs['error'][] = $e->getMessage();
                }
            }
        }
        return $paginator->getElementLimit();
    }
}