<?php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class Certbot {

  private $mc;
  private $http;

  public function index(Request $request, Response $response) {
    session_start();

    $state = mt_rand(10000,99999);
    $_SESSION['state'] = $state;

    $response->setContent(p3k\XRay\view('certbot', [
      'title' => 'X-Ray',
      'state' => $state
    ]));
    return $response;
  }

  public function start_auth(Request $request, Response $response) {
    session_start();

    $_SESSION['client_id'] = $request->get('client_id');
    $_SESSION['redirect_uri'] = $request->get('redirect_uri');

    $query = http_build_query([
      'me' => $request->get('me'),
      'client_id' => $request->get('client_id'),
      'redirect_uri' => $request->get('redirect_uri'),
      'state' => $request->get('state'),
    ]);

    $response->headers->set('Location', 'https://indieauth.com/auth?'.$query);
    $response->setStatusCode(302);
    return $response;
  }

  public function redirect(Request $request, Response $response) {
    session_start();

    $this->http = new p3k\HTTP();

    if(!isset($_SESSION['state']) || $_SESSION['state'] != $request->get('state')) {
      $response->headers->set('Location', '/cert?error=invalid_state');
      $response->setStatusCode(302);
      return $response;
    }

    if($code = $request->get('code')) {

      $res = $this->http->post('https://indieauth.com/auth', http_build_query([
        'code' => $code,
        'client_id' => $_SESSION['client_id'],
        'redirect_uri' => $_SESSION['redirect_uri'],
        'state' => $_SESSION['state']
      ]), [
        'Accept: application/json'
      ]);
      $verify = json_decode($res['body'], true);

      unset($_SESSION['state']);

      if(isset($verify['me'])) {

        if(in_array($verify['me'], Config::$admins)) {
          $_SESSION['me'] = $verify['me'];
          $response->headers->set('Location', '/cert');
        } else {
          $response->headers->set('Location', '/cert?error=invalid_user');
        }

      } else {
        $response->headers->set('Location', '/cert?error=invalid');
      }

    } else {
      $response->headers->set('Location', '/cert?error=missing_code');
    }

    $response->setStatusCode(302);
    return $response;
  }

  public function save_challenge(Request $request, Response $response) {
    session_start();

    if(!isset($_SESSION['me']) || !in_array($_SESSION['me'], Config::$admins)) {
      $response->headers->set('Location', '/cert?error=forbidden');
      $response->setStatusCode(302);
      return $response;
    }

    $token = $request->get('token');
    $challenge = $request->get('challenge');

    if(preg_match('/acme-challenge\/(.+)/', $token, $match)) {
      $token = $match[1];
    } elseif(!preg_match('/^[_a-zA-Z0-9]+$/', $token)) {
      echo "Invalid token format\n";
      die();
    }

    $this->_mc();
    $this->mc->set('acme-challenge-'.$token, json_encode([
      'token' => $token,
      'challenge' => $challenge
    ]), 0, 600);

    $response->setContent(p3k\XRay\view('certbot', [
      'title' => 'X-Ray',
      'challenge' => $challenge,
      'token' => $token,
      'verified' => true
    ]));
    return $response;
  }

  public function logout(Request $request, Response $response) {
    session_start();
    unset($_SESSION['me']);
    unset($_SESSION['client_id']);
    unset($_SESSION['redirect_uri']);
    unset($_SESSION['state']);
    session_destroy();
    $response->headers->set('Location', '/cert');
    $response->setStatusCode(302);
    return $response;
  }

  public function challenge(Request $request, Response $response, array $args) {
    $this->_mc();

    $token = $args['token'];

    if($cache = $this->mc->get('acme-challenge-'.$token)) {
      $acme = json_decode($cache, true);

      $response->setContent($acme['challenge']);
    } else {
      $response->setStatusCode(404);
      $response->setContent("Not Found\n");
    }

    $response->headers->set('Content-Type', 'text/plain');
    return $response;
  }

  private function _mc() {
    $this->mc = new Memcache();
    $this->mc->addServer('127.0.0.1');
  }

}