<?php require_once '../src/kernel.php';

/*
  - all code in this file is in the root namespace (as is code in kernel.php)
  - this file contains dynamic routes, that may or may not correspond to an actual page.
  - anonymous functions are bound to the Response object (use `$this` to manipulate Response)
  - All error handling is configured in this page in the catch blocks
*/

// This is a dumb/handy route for dealing with services that do not support CORS (formstack)
// currenently it is used when embedding iframes so the data can be accessed by js on our end


Core\Route::set('proxy', fn($url) => file_get_contents(base64_decode($url)));


// Setting robots file in config
Core\Route::set('robots', fn() => file_get_contents(Controller\System::config('robots')));

Core\Route::set('tnew', function() {
  $this->header('X-Robots-Tag', 'noindex');
  
  Render::set('after', function($d) {
    foreach($d->find('//body/footer//a[starts-with(@href, "/")]/@href') as $link) $link->value = "https://mcachicago.org{$link}";
  });

  return new DOM\Document('<div id="TNEW"><!-- TNEW content here --></div>');
}, ['layout' => 'layout/tnew.html']);

// Post Request from AWS. Though message is JSON, request type is text/plain; manually decode 
Core\Route::set('press', new Core\Controller('Controller\Press'));


// TODO revisit and turn into a general sitemap
Core\Route::set('corpus', function($type = 'href') {

  $query = '//ol[@title]/li' . Model::config('query');

  if ($this->request->type == 'xml') {
    $this->header('cache-control', sprintf('max-age=%d', 86400));
    $this->pages = Model::DB()->map($query, fn($item) => Model\Vertex::Factory($item->getAttribute('id'), 'link'));

    return DOM\Document::open('layout/sitemap.xml');
  }

  $lines = [];
  
  foreach(Model::DB($query, 'find') as $node) {
    $id   = substr_replace($node->getAttribute('id'), '/', 2, 0);
    $link = '../data/links/' . $id;
    if ($ref = $type == 'href' ? (is_link($link) ? substr(readlink($link), 11, -5) : false) : "../{$id}.md") {
      $lines[] =  [$ref, $node->nodeValue, $node->select('../@title')->value];
    }
  }
  
  return json_encode($lines);
});

Core\Route::taxonomy(function() {
  $model = DOM\Document::open('../data/model.xml');
  $summary = new DOM\Document('<article/>');

  $deets = function($doc, $ctx, $open = false, $q = './*[*/@id]') use(&$deets) {
    foreach($doc->find($q) as $node) {
      $d = $ctx->appendChild(new DOM\Element('details'));
      $d->appendChild(new DOM\Element('summary', (string)$node['@id']));
      if ($open) $d->setAttribute('open', 'true');
      
      if ($node->select($q)) {
        $deets($node, $d);
      } else {
        $list = $d->appendChild(new DOM\Element('ol'));
        foreach ($node->find('li') as $page) {
          $li = $list->appendChild(new DOM\Element('li'));
          $li->appendChild(new DOM\Element('code', $page->getAttribute('id')));
          $li->appendChild(new DOM\Element('span', $page->nodeValue));
        } 
      }
    }

  };
  
  $deets($model->select('/'), $summary->firstChild, true);
  return $summary;
});

Core\Route::set('search', function($corpus = false) {
  $config  = Util\API::config('google');
  $this->query = $this->input['query'] ?? throw $this->status(404);
  $request = HTTP::GET($config['url'])->send([
    'q' => urlencode($this->query),
    'cx' => $config['cx'],
    'key' => $config['key'],
    'fields' => 'items(link,title,snippet)',
  ]);
  
  $this->items = array_filter(array_map(fn($r) => [
    'link'    => str_replace('https://mcachicago.org', '', $r['link']),
    'title'   => preg_replace('/(?: - )?MCA(?: - )?/', '', $r['title']),
    'snippet' => $r['snippet'],
    // 'img'     => $r['pagemap']['cse_image'][0]['src'] ?? null,
  ], $request->resource['items'] ?? []), fn($d) => $d['link'] != '/' || ! str_starts_with($d['link'], 'https://visit.mca'));

  $this->header('X-Robots-Tag', 'noindex');
  return DOM\Document::open('layout/landing/search.html');
});

// TODO consider moving webhooks into controller\System
Core\Route::set('reporting', function($type = 'request', ?array $input = null) {

  if(! $input) {
    $this->status(200); // bypass the next action by setting the status to success now
    $this->form = ['bug' => 'bugreporting', 'request' => 'add_change_content'][$type];
    return DOM\Document::open('forms/formstack-embed.html');
  }
  
  Auth\Token::HMAC($this, 'X_FS_SIGNATURE') ?: throw $this->status(404);
  
  return ['data' => $input, 'type' => $type];
  
})->then(function($data, $type) {
  
  Util\Bench::Task([new Service\Formstack($data, $type), 'process']);
  
  return new DOM\Document('<p>ok</p>');  
});



Core\Route::set('calendar', function(?int  $year = null, ?int $month = null, ?string $slug = null) {
  
  array_push($this->data['breadcrumbs'], ['url' => '/calendar', 'name' => 'Calendar']);
   
  if ($this->request->origin != '/calendar') {
    $this->yield('breadcrumbs' , 'layout/breadcrumbs.html');
  }
    
  if (! $slug && ($year || $month || key_exists('when', $this->input))) {
    
    $basic = ['when' => null, 'page' => 1, 'filter' => null, 'tag' => null, 'limit' => 36, 'order' => 'asc'];
    $query = array_replace($basic, array_intersect_key($this->input, $basic));
    $query += ['type' => 'events', 'year' => $year, 'month' => $month];
    $this->type     = 'events';
    // $this->heading  = $query['when'] ??  'Events' ;
    $events = Model\Content\Calendar::Build(...$query);
    if ($group = $this->input['group'] ?? false) {
      $this->calendar = \dict\group($group, $events, 'ksort');
      return DOM\Document::open('layout/calendar-group.html');
    }
    $this->calendar = $events;
    
    return DOM\Document::open('layout/calendar.html', cache: false);
  }

  array_push($this->data['breadcrumbs'], [
    'url' => sprintf('/calendar/%s/%s', $year ?? date('Y'), $month ?? date('m')),
    'name' => date('F Y', mktime(0, 0, 0, $month, 1, $year)),
  ]);

  if ($this->request->type == 'ics' && ($event = Model\Vertex::Factory('/'.$this->request->origin)) && $event instanceof Model\Content\Calendar) {
    
    $idx   = $this->input['idx'] ?? 0;
    return new \vendor\ICS([
      'description' => wordwrap($event->document->select(Render::META['description'])->nodeValue ?? '', 75, "\\n"),
      'dtstart'     => $event->occurrence('start', $idx)->format(\Datetime::RFC3339),
      'dtend'       => $event->occurrence('end', $idx)->format(\Datetime::RFC3339),
      'location'    => join(' ', array_map(fn($e) => $e->title, $event->locations)),
      'summary'     => $event->title,
      'url'         => $this::config('host') . $event->permalink,
    ]);
    
  }
  
})->catch(function($e) {
  error_log("/{$this->request->origin}: {$e->getMessage()}");
  throw new RuntimeException("URL parameters are incorrect", 400);
});




Core\Route::set('exhibitions', function($year = null, ?string $slug = null) {
  array_push($this->data['breadcrumbs'], ['url' => '/exhibitions', 'name' => 'Exhibitions']);

  if ($year != $slug) {
    $this->yield('breadcrumbs', 'layout/breadcrumbs.html');
  }
  
  
  if ($year == 'archive') {
    $current = floor(date('Y') / 10) * 10;
    $slug  ??= $current;
    
    $decades = [];
    $decade = 1950;

    while ($decade < $current) {
      $decades[] = [
        'decade'   => $decade += 10,
        'link'   => "/exhibitions/archive/{$decade}",
        'active' => $decade == $slug ? 'active' : null,
      ];
      
    }

    $this->decades = $decades;
    $this->decade = $slug;
    $this->exhibitions = array_reverse(dict\group('year', Model\Content\Calendar::Exhibitions([$slug, $slug + 10]), true));
    $this->size = count($this->exhibitions);
    return DOM\Document::open('layout/exhibitions.html');
  }
  
  
  if ($year && ! is_numeric($year)) {
    
    if ($year == 'series') {
      if ($slug) {
        array_push($this->data['breadcrumbs'], ['url' => '/exhibitions/series', 'name' => 'Series']);
      } else {
        $this->series = \Model\Agent\Topic::List('series')->filter(fn($m) => str_contains($m->permalink, 'exhibitions'));
        $this->yield('series', 'layout/exhibition-series.html');
      }
    }
    
  } else {

    
    if (! $slug && ($year || key_exists('when', $this->input))) {
      
      $basic = ['when' => null, 'page' => 1, 'filter' => null, 'order' => 'asc'];
      $query = array_replace($basic, array_intersect_key($this->input, $basic));
      $query += ['type' => 'exhibitions', 'year' => $year];
      
      $this->type     = 'exhibitions';
      // $this->heading  = 'TODO: better headings';
      
      $this->calendar = Model\Content\Calendar::Build(...$query);
      
      if ($this->calendar->length > 0) {
        $doc = DOM\Document::open('layout/calendar.html', cache: false);
      } else {
        $doc = new \DOM\Document('<aside class="">No Exhibitions Available</aside>');
      }
      return $doc;
    }
  
  }
  
});

Core\Route::set('graphics', function(...$path) {
  $this->header('cache-control', sprintf('max-age=%d', 86400));
  
  $this->data += $this->input + ['fill' => '#000'];

  $path = 'ux/graphics/' . join('/', $path);

  if ($this->request->type == 'css') {
    
    $tmp = "  --%s: url('data:image/svg+xml;base64,%s');\n";
    $out = array_reduce(glob($path . '/*.svg'), fn($c, $i) => $c .= sprintf($tmp, basename($i, '.svg'), base64_encode(\DOM\Template::load($i, $this->data))), '');
    
    return ":root {\n{$out}\n}";
  }

  return \DOM\Document::open($path . '.svg');
});

Core\Route::set('metadata', function(string $type, string $id = null) {
  $data = \DOM\Render\Plain::check(\DOM\Template::load("layout/api/{$type}.xml", \Model\Vertex::Factory($id, 'links')), 'json');
  return $this->request->type == 'js'
       ? $data
       : new \DOM\Document("<script type='application/ld+json'>{$data}</script>");
});

Core\Route::set('olx', function(string $version, string $id, ?string $round = null) {
  $this->form_id = $id;
  $this->yield('form', "forms/support/olx/{$version}.html");
  if ($round) {
    $this->yield('round', 'forms/support/olx/round.html');
  }
  return \DOM\Document::open("forms/support/olx/layout.html");
});


#  I M P L E M E N T A I O N


try {
  
  $request = new Core\Request(headers: $_SERVER, root: realpath('.'));
  
  $data = [
    'breadcrumbs' => [['name' => 'MCA', 'url' => '/']],
    'expires'     => fn($t, $c = 'today') => strtotime($t) < strtotime($c) ? 'inactive' : "through {$t}",
    'prefs'       => $request->origin == 'index' ? null : $_COOKIE['prefs'] ?? 'theme-auto', // homepage is cached!
  ];

  Controller\System::load($request);


  
  $response = new Core\Response($request, $data);
  
  Render::set('after', function($d) use($response) {
    $exp = "/html/body/header/nav/ul/li/span[a[@href!='/{$response->route}']]/following-sibling::ul";
    foreach($d->find($exp) as $node) $node->remove();
  });

  $response = Core\Route::delegate($response);

} catch (Core\Status $notice) {

  $code   = $notice->getCode();

  

  $response = match((int)floor($code / 100)) {
    2, 5    => new Core\Response($notice->request), // original response is unnecessary, boom boom
    3       => $notice->request->redirect($notice->getMessage(), $code, $notice->response),
    default => $notice->response ?? Controller\System::respond($notice->request, ['error', $code], $data + ['message' => $notice->getMessage()]),
  };
  
} catch (RuntimeException $e) {
  
  $response = Controller\System::respond($request, ['error', $e->getCode() ?: 503], ['message' => $e->getMessage()]);

} catch (Exception | Error $e) {
  
  $response = Controller\System::respond($request, ['error', $e->getCode() ?: 500], Core\Status::trace($e));

} finally {
  
  ob_start();

  echo $response;
 
  ob_end_flush();
  flush();
  
  if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
  
  Util\Bench::Clear();
}