<?php

const SELF_BASE = 'https://tools.entities.org.uk/pawpub-experiments/yuribot.php';
const API_BASE = 'https://danbooru.donmai.us';
const QUERY = 'yuri+rating:general+order:random';
const DB_FILE = '/home/winter/yuribot_posts.json';
const BANNED_TAGS = ['meme', 'nazi', 'military', 'war'];

function has_banned_tags(stdClass $post): bool {
    foreach (BANNED_TAGS as $tag) {
        if (str_contains($post->tag_string, $tag)) return true;
    }
    return false;
}

function fetch_post(): ?stdClass {
    $context = stream_context_create(['http' => [
        'user_agent' => 'pawpub yuribot <https://tools.entities.org.uk/pawpub-experiments/yuribot.php>'
    ]]);
    $response = file_get_contents(API_BASE . '/posts.json?tags=' . QUERY . '&limit=1', context: $context);
    if (!$response) return null;
    $posts = json_decode($response);
    if (!$posts) return null;
    if (has_banned_tags($posts[0])) return null;
    return $posts[0];
}

function fetch_specific_post(int $id): ?stdClass {
    $context = stream_context_create(['http' => [
        'user_agent' => 'pawpub yuribot <https://tools.entities.org.uk/pawpub-experiments/yuribot.php>'
    ]]);
    $response = file_get_contents(API_BASE . "/posts/$id.json", context: $context);
    if (!$response) return null;
    $post = json_decode($response);
    if (!$post) return null;
    if (has_banned_tags($post)) return null;
    return $post;
}

function fetch_post_from_db(array $myPosts, int $id): ?stdClass {
    $uri = SELF_BASE . '?id=' . $id;
    foreach ($myPosts as $post) {
        if ($post->{'self'} == $uri) return $post;
    }
    return null;
}

function format_tags(string $tags): string {
    // "tag_1 tag_2" -> "tag 1, tag 2"
    return str_replace('_', ' ', str_replace(' ', ', ', $tags));
}

function make_link(string $label, string $href, bool $markdown): string {
    if ($markdown) {
        return "[$label]($href)";
    } else {
        return "$label: $href";
    }
}

function post_to_content(stdClass $post, bool $markdown): string {
    $artists = format_tags($post->tag_string_artist);
    $characters = format_tags($post->tag_string_character);
    $media = format_tags($post->tag_string_copyright);
    $source = make_link('Source', $post->source, $markdown);
    $danbooru = make_link('Danbooru post', API_BASE . "/posts/$post->id", $markdown);
    
    return <<<EOF
    Artist(s): $artists
    Characters: $characters
    Media: $media

    $source / $danbooru
    EOF;
}

function post_to_ppnote(stdClass $post): array {
    $file = $post->media_asset->variants[2];
    return [
        'type' => 'note',
        'self' => SELF_BASE . "?id=$post->id",
        'created' => (new DateTimeImmutable)->format(DATE_ISO8601),
        'author' => SELF_BASE,
        'plainContent' => post_to_content($post, false),
        'formattedContent' => [
            'text/markdown' => post_to_content($post, true)
        ],
        'language' => 'en',
        'privacy' => ['scope' => 'public', 'indexable' => false],
        'attachments' => [[
            'type' => "image/$file->file_ext",
            'href' => $file->url,
            'description' => 'Danbooru tags: ' . format_tags($post->tag_string_general)
        ]]
    ];
}

function get_my_posts_internal(): array {
    if (is_readable(DB_FILE)) {
        return json_decode(file_get_contents(DB_FILE)) ?? [];
    }
    return [];
}

function get_my_posts(): array {
    $posts = get_my_posts_internal();
    if (needs_new_post($posts)) {
        while (!isset($newPost)) {
            $newPost = fetch_post();
            sleep(0.25); // avoid ratelimit
        }
        $posts[] = post_to_ppnote($newPost);
        save_my_posts($posts);
    }
    return $posts;
}

function save_my_posts(array &$posts) {
    while (count($posts) > 12) {
        array_shift($posts);
    }
    file_put_contents(DB_FILE, json_encode($posts));
}

function needs_new_post(array &$posts) {
    if (count($posts) == 0) return true;
    $lastDate = new DateTimeImmutable(array_slice($posts, -1)[0]->created);
    return (new DateTimeImmutable)->getTimestamp() - $lastDate->getTimestamp() > (60*60 /* 1 hour */);
}


// ---- main ---------------------

header('Content-Type: application/json');
header('Cache-Control: no-cache');

if (isset($_GET['id'])) {
    $myPosts = get_my_posts();
    header('Cache-Control: max-age=36000');
    echo json_encode(fetch_post_from_db($myPosts, $_GET['id']));
} elseif (isset($_GET['basicFeed'])) {
    $myPosts = array_slice(get_my_posts(), -5);
    echo json_encode([
        'page' => 1,
        'totalPages' => 1,
        'totalItems' => count($myPosts),
        'items' => array_map(fn($post) => $post->{'self'}, $myPosts)
    ]);
} elseif (isset($_GET['fullFeed'])) {
    $myPosts = array_slice(get_my_posts(), -5);
    echo json_encode([
        'page' => 1,
        'totalPages' => 1,
        'totalItems' => count($myPosts),
        'items' => $myPosts
    ]);
} else {
    header('Cache-Control: max-age=36000');
    echo json_encode([
        'type' => 'actor',
        'self' => SELF_BASE,
        'created' => '2025-05-11T21:44:00+01:00',
        'handle' => 'yuribot',
        'displayName' => 'yuri bot',
        'bio' => "posts random sfw yuri every hour\nposts are only served for 12 hours after publishing\n\navatar source: https://danbooru.donmai.us/posts/6675581",
        'avatar' => 'https://tools.entities.org.uk/pawpub-experiments/yuribot-avatar.png',
        'automated' => true,
        'endpoints' => [
            'basicFeed' => SELF_BASE . '?basicFeed=1',
            'fullFeed' => SELF_BASE . '?fullFeed=1'
        ]
    ]);
}

