<?php

namespace Citrus;

// Secret, указанный для хука в Gogs!!
define('GOGS_SECRET', 'cp.citrus-$ecret');

/**
 * В URL к этому скрипту можно указать GET-параметр force.
 * В таком случае при каждом pull в рабочей копии будут принудительно сброшены все незафиксированные изменения
 */

function set_status($status)
{
	if (stristr(PHP_SAPI, "cgi") !== false)
	{
		header("Status: " . $status);
	}
	else
	{
		header($_SERVER["SERVER_PROTOCOL"] . " " . $status);
	}
}

function cmd($cmd)
{
	exec(sprintf('cd %s && %s 2>&1', $_SERVER['DOCUMENT_ROOT'], $cmd), $output, $exitCode);
	if ($exitCode !== 0)
	{
		throw new \RuntimeException(sprintf('Command `%s` failed [%d]: %s', $cmd, $exitCode, implode(PHP_EOL, $output)));
	}
	return implode(PHP_EOL, $output);
}

function git_current_branch()
{
	$output = cmd('git branch');
	if (preg_match('/\\* (.*)/', $output, $matches))
	{
		return $matches[1];
	}
}

/**
 * @param string $message
 * @param array $payload
 * @param int $code
 * @return string
 */
function json_response($message = null, $code = 200, array $payload = null)
{
	static $status = array(
		200 => 'OK',
		400 => 'Bad Request',
		401 => 'Unauthorized',
		422 => 'Unprocessable Entity',
		500 => 'Internal Server Error',
	);

	// clear the old headers
	header_remove();
	// set the actual code
	http_response_code($code);
	// set the header to make sure cache is forced
	header("Cache-Control: no-transform,public,max-age=300,s-maxage=900");
	// treat this as json
	header('Content-Type: application/json');

	// ok, validation error, or failure
	set_status($code . ' ' . $status[$code]);

	$response = array(
		'status' => $code < 300, // success or not?
		'message' => $message ? $message : $status[$code],
	);
	if (isset($payload))
	{
		$response += is_array($payload) ? $payload : compact('payload');
	}

	// return the encoded json
	return json_encode($response);
}

try
{
	$handle = fopen('php://input', 'rb');
	$requestPayload = stream_get_contents($handle);
	$event = json_decode($requestPayload, true);
	if (!is_array($event))
	{
		die(json_response('Error decoding json request', 400));
	}
	fclose($handle);

	/**
	 * Gogs передает SHA256 HMAC тела запроса в заголовке X-Gogs-Signature
	 */
	if (defined('GOGS_SECRET'))
	{
		if (!function_exists('hash_hmac'))
		{
			die(json_response('GOGS_SECRET reques Hash extension (http://php.net/manual/hash.installation.php)', 500));
		}

		$requestPayload = str_replace(PHP_EOL, "\n", $requestPayload);
		if (hash_hmac('sha256', $requestPayload, GOGS_SECRET) != $_SERVER['HTTP_X_GOGS_SIGNATURE'])
		{
			die(json_response('Secret key mismatch', 401));
		}
	}

	$remoteBranch = $event['ref'];
	$remoteBranch = str_replace('refs/heads/', '', $remoteBranch);
	$localBranch = git_current_branch();

	if ($remoteBranch == $localBranch)
	{
		if (isset($_GET['force']))
		{
			cmd('git reset --hard HEAD && git clean -d -f -q -e ' . basename(__FILE__));
		}
		$output = cmd('git pull');
		echo json_response('Pull success', 200, compact('remoteBranch', 'localBranch', 'output'));
	}
	else
	{
		echo json_response('It\'s not our branch', 200, compact('remoteBranch', 'localBranch'));
	}
}
catch (\Exception $e)
{
	echo json_response($e->getMessage(), 500);
	die();
}
