use Modern::Perl;
use XML::LibXML;
use DateTime;
use File::Slurper qw(read_text write_binary);
use List::Util qw(any);
use Mojo::Util qw(url_escape);
use Encode;

binmode STDOUT, ":utf8";

die "No items to add.\n" unless @ARGV;
die "Cannot read all the files.\n" if any { ! -r } @ARGV;

sub html {
  my $pn = shift;
  my $html = qx(oddmu html $pn);
  return $html if $? == 0;
  die $html;
}

my $base = 'https://alexschroeder.ch/view/';

sub feed {
  my ($doc, @files) = @_;
  my @items;
  for my $fn (@files) {
    my $md = read_text($fn);
    die "Could not read file content for $fn\n" unless $md;
    my ($title) = $md =~ /^# (.*)/m;
    die "Could not determine title for $fn\n" unless $title;
    my ($pn) = $fn =~ /(.*)\.md$/;
    die "Could not determine page name for $fn\n" unless $pn;
    my $url = $base . url_escape($pn);
    if ($doc->find(qq(//link[text()="$url"]))) {
      say "$pn is already in the feed";
      next;
    }
    my $pd = DateTime->now->strftime("%a, %d %b %Y %H:%M:%S %z");
    my $item = $doc->createElement('item');
    my $channel = $doc->getElementsByTagName("channel")->shift;
    my $first_item = $doc->getElementsByTagName("item")->shift;
    if ($first_item) {
      $channel->insertBefore($item, $first_item);
    } else {
      $channel->appendChild($item);
    }
    $channel->insertAfter($doc->createTextNode("\n\n"), $item);
    push(@items, $item);
    $item->appendText("\n");
    $item->appendChild($doc->createElement('title'))->appendText($title);
    $item->appendText("\n");
    $item->appendChild($doc->createElement('link'))->appendText($url);
    $item->appendText("\n");
    $item->appendChild($doc->createElement('guid'))->appendText($url);
    $item->appendText("\n");
    $item->appendChild($doc->createElement('description'))->appendText(html($pn));
    $item->appendText("\n");
    $item->appendChild($doc->createElement('pubDate'))->appendText($pd);
    while ($md =~ /(?:^|\s)#(\S+)/g) {
      $item->appendText("\n");
      $item->appendChild($doc->createElement('category'))->appendText($1);
    }
    $item->appendText("\n");
  }
  return @items;
}

sub adopt_item {
  my ($doc, $item) = @_;
  my $url = $item->getElementsByTagName("link")->shift->textContent;
  if ($doc->find(qq(//link[text()="$url"]))) {
    my $title = decode_utf($item->getElementsByTagName("title")->shift->textContent);
    say "'$title' is already in " . $doc->URI;
    return;
  }
  my $channel = $doc->getElementsByTagName("channel")->shift;
  my $first_item = $doc->getElementsByTagName("item")->shift;
  my $import = $doc->importNode($item);
  if ($first_item) {
    $channel->insertBefore($import, $first_item);
  } else {
    $channel->appendChild($import);
  }
  $channel->insertAfter($doc->createTextNode("\n\n"), $import);
  return 1;
}

sub save {
  my ($file, $data) = @_;
  rename($file, $file . "~");
  write_binary($file, $data);
  say "Wrote $file";
}

# Add to main feed
my $feed = "feed.xml";
my $doc = XML::LibXML->load_xml(location => $feed);
my @items = feed($doc, @ARGV);
save($feed, $doc) if @items;

# Add to items to a lower case XML feed if it exists
my %feeds;
for my $item (@items) {
  for my $category (map { $_->textContent } $item->getElementsByTagName("category")) {
    $feed = lc($category) . ".xml";
    next unless -f $feed;
    $doc = $feeds{$feed};
    $doc = XML::LibXML->load_xml(location => $feed) unless $doc;
    $feeds{$feed} = $doc if adopt_item($doc, $item);
  }
}

# write every XML file just once
for $feed (keys %feeds) {
  save($feed, $feeds{$feed});
}

# Add menu links to the Diary, or a category page if it exists
my %files;
for my $item (@items) {
  for my $category ("index", map { $_->textContent } $item->getElementsByTagName("category")) {
    my $file = "$category.md";
    next unless -f $file;
    my $title = $item->getElementsByTagName("title")->shift->textContent;
    my $name = $item->getElementsByTagName("link")->shift->textContent;
    $name =~ s/^$base//;
    my $line = "* [$title]($name)\n";
    my $md = $files{$file};
    $md = read_text($file) unless $md;
    next if index($md, $line) != -1;
    # insert before first list item or hashtag or append
    $md =~ s/^(\* )/$line$1/m or $md =~ s/^(#\S)/$line\n$1/m or $md .= "\n\n$line";
    $files{$file} = $md;
  }
}

# write every file just once
for my $file (keys %files) {
  save($file, encode_utf8($files{$file}));
}
