<?php
namespace Misuzu;

function ghcb_strip_prefix(string $line): string {
    $findColon = mb_strpos($line, ':');
    return trim($findColon === false || $findColon >= 10 ? $line : mb_substr($line, $findColon + 1));
}

function ghcb_changelog_action(string &$line): string {
    $original = trim($line);
    $line = ghcb_strip_prefix($line);

    $firstSix = mb_strtolower(mb_substr($original, 0, 6));

    if($firstSix === 'revert'
        || $firstSix === 'restor')
        return 'revert';
    if($firstSix === 'import')
        return 'import';

    $firstThree = mb_strtolower(mb_substr($original, 0, 3));

    if($firstThree === 'add'
        || $firstSix === 'create')
        return 'add';
    if($firstThree === 'fix')
        return 'fix';

    $firstFour = mb_strtolower(mb_substr($original, 0, 4));
    $firstEight = mb_strtolower(mb_substr($original, 0, 8));

    if($firstSix === 'delete'
        || $firstSix === 'remove'
        || $firstFour === 'nuke'
        || $firstEight === 'dropkick')
        return 'remove';

    return 'update';
}

header('Content-Type: text/plain; charset=utf-8');

if($_SERVER['REQUEST_METHOD'] !== 'POST')
    die('no');

$config = MSZ_ROOT . '/config/github.ini';
if(!is_file($config))
    die('config missing');

$config = parse_ini_file(MSZ_ROOT . '/config/github.ini', true);
if(empty($config['tokens']['token']))
    die('config invalid');

$isGitea = isset($_SERVER['HTTP_X_GITEA_DELIVERY']) && isset($_SERVER['HTTP_X_GITEA_EVENT']);

$rawData = file_get_contents('php://input');
$sigParts = $isGitea
    ? ['sha256', $_SERVER['HTTP_X_GITEA_SIGNATURE']]
    : explode('=', $_SERVER['HTTP_X_HUB_SIGNATURE'] ?? '', 2);

if(empty($sigParts[1]))
    die('invalid signature');

$repoAuthenticated = false;
foreach($config['tokens']['token'] as $repoName => $repoToken) {
    if(hash_equals(hash_hmac($sigParts[0], $rawData, $repoToken), $sigParts[1])) {
        $repoAuthenticated = true;
        break;
    }
}

if(!$repoAuthenticated)
    die('signature check failed');

$data = json_decode($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'
    ? $_POST['payload']
    : $rawData);

if(empty($data))
    die('body is corrupt');

if(empty($data->repository->full_name))
    die('body is corrupt');

if($data->repository->full_name !== $repoName)
    die('invalid repository token');

if($_SERVER['HTTP_X_GITHUB_EVENT'] !== 'push')
    die('only push event is supported');

$commitCount = count($data->commits);
if($commitCount < 1)
    die('no commits received');

$repoInfo = $config['repo:' . $repoName] ?? [];

$repoMaster = 'refs/heads/master';
if(!empty($repoInfo['master']))
    $repoMaster = $repoInfo['master'];

if($data->ref !== $repoMaster)
    die('only the master branch is tracked');

$changelog = $msz->getChangelog();

$tags = $repoInfo['tags'] ?? [];
$addresses = $config['addresses'] ?? [];

foreach($data->commits as $commit) {
    $message = trim($commit->message);

    if(mb_strpos($message, $repoName) !== false
        || mb_substr($message, 0, 2) === '//'
        || mb_strpos(mb_strtolower($message), 'merge pull request') !== false)
        continue;

    $index = mb_strpos($message, "\n");
    $line = $index === false ? $message : mb_substr($message, 0, $index);
    $body = trim($index === false ? '' : mb_substr($message, $index + 1));

    $changeInfo = $changelog->createChange(
        ghcb_changelog_action($line),
        $line, $body,
        $addresses[$commit->author->email] ?? null,
        max(1, strtotime($commit->timestamp))
    );

    if(!empty($tags))
        foreach($tags as $tag)
            $changelog->addTagToChange($changeInfo, $tag);
}
