<?php
/**
 * Perforce Swarm
 *
 * @copyright   2015 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level readme folder of this distribution.
 * @version     <release>/<patch>
 */

namespace Slack;

use Activity\Model\Activity;
use Comments\Model\Comment;
use P4\Spec\Change;
use Projects\Model\Project;
use Zend\Mvc\MvcEvent;
use Zend\Http\Client;
use Zend\Http\Request;
use Zend\Json\Json;


class Module
{
    /**
     * Connect to queue event manager to handle changes.
     *
     * @param   MvcEvent $event the bootstrap event
     * @return  void
     */
    public function onBootstrap(MvcEvent $event)
    {
        $application = $event->getApplication();
        $services = $application->getServiceManager();
        $manager = $services->get('queue');
        $events = $manager->getEventManager();
        $logger = $services->get('logger');

        $filters = $services->get('InputFilterManager');
        $projectFilter = $filters->get('ProjectFilter');

        $projectFilter->add(
            array(
                'name' => 'slack',
                'required' => false,
                'filters' => array(
                    array(
                        'name' => 'Callback',
                        'options' => array(
                            'callback' => function ($value) {
                                $value = (array)$value + array('enabled' => false, 'url' => '');
                                return array(
                                    'enabled' => (bool)$value['enabled'],
                                    'url' => (string)$value['url']
                                );
                            }
                        )
                    )
                )
            )
        );

        $filters->setService('ProjectFilter', $projectFilter);

        $helpers = $services->get('ViewHelperManager');
        $helpers->get('headScript')->prependFile('/module/Slack/js/Slack.js', $type = 'text/javascript');

        // connect to all tasks and write activity data
        // we do this late (low-priority) so all handlers have
        // a chance to influence the activity model.
        $events->attach(
            array('task.commit', 'task.change'),
            function ($event) use ($services, $logger) {

                $logger->info("Slack: activity...");

                // task.change doesn't include the change object; fetch it if we need to
                $p4Admin = $services->get('p4_admin');
                $change = $event->getParam('change');
                if (!$change instanceof Change) {
                    try {
                        $change = Change::fetch($event->getParam('id'), $p4Admin);
                        $event->setParam('change', $change);
                    } catch (SpecNotFoundException $e) {
                    } catch (\InvalidArgumentException $e) {
                    }
                }

                // if this isn't a submitted change; nothing to do
                if (!$change instanceof Change || !$change->isSubmitted()) {
                    $logger->info("Slack: not a change...");
                    return;
                }

                // prepare list of projects affected by the change
                $impacted = Project::getAffectedByChange($change, $p4Admin);

                if ($impacted) {

                    $logger->info("Slack: impacted...");

                    $projects = Project::fetchAll(array(Project::FETCH_BY_IDS => array_keys($impacted)), $p4Admin);
                    foreach ($projects as $projectId => $project) {

                        $logger->info("Slack: impacted: $projectId");

                        // URL to POST messages to Slack
                        $slack = $project->getRawValue('slack');
                        $enabled = $slack['enabled'];
                        $url = $slack['url'];

                        // Skip projects with no Slack URL                        
                        if (!$enabled || !isset($url) || trim($url) === '') {
                            continue;
                        }

                        // Host address of Swarm for link back URLs
                        $host = $services->get('viewhelpermanager')->get('qualifiedUrl');

                        // Icon and User for messages in feed Slack
                        $config = $services->get('config');
                        $max = (int) $config['slack']['max_length'];

                        $message = new Message($host, $change, $max);
                        $text = $message->toString();
                        $this->postSlack($url, $text, $services);
                    }

                }

                $logger->info("Slack: end.");
            },
            // set our priority, -110 puts us right before the end, but before the activity is published
            -110
        );
    }

    private function postSlack($url, $msg, $services)
    {
        $logger = $services->get('logger');
        $config = $services->get('config');

        $icon = $config['slack']['icon'];
        $user = $config['slack']['user'];

        $logger->info("Slack: POST to $url");
        $logger->info("Slack: user=$user");
        $logger->info("Slack: icon=$icon");

        try {
            $json = array(
    			'payload' => json_encode(
                    array(
                        'username' => $user,
                        'icon_url' => $icon,
                        'text' => $msg
                    )
                )
            );

			$request = new Request();
            $request->setMethod('POST');
			$request->setUri($url);
			$request->getPost()->fromArray($json);

			$client = new Client();
			$client->setEncType(Client::ENC_FORMDATA);

            // set the http client options; including any special overrides for our host
            $options = $config + array('http_client_options' => array());
            $options = (array) $options['http_client_options'];
            if (isset($options['hosts'][$client->getUri()->getHost()])) {
                $options = (array) $options['hosts'][$client->getUri()->getHost()] + $options;
            }
            unset($options['hosts']);
            $client->setOptions($options);

            // POST request
			$response = $client->dispatch($request);

            if (!$response->isSuccess()) {
                $logger->err(
                    'Slack failed to POST resource: ' . $url . ' (' .
                    $response->getStatusCode() . " - " . $response->getReasonPhrase() . ').',
                    array(
                        'request'   => $client->getLastRawRequest(),
                        'response'  => $client->getLastRawResponse()
                    )
                );
                return false;
            }
            return true;

        } catch (\Exception $e) {
            $logger->err($e);
        }
    }


    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }
}