<?php

use dokuwiki\plugin\cachestats\FileStatistics;
use splitbrain\phpcli\Options;
use splitbrain\phpcli\TableFormatter;

/**
 * DokuWiki Plugin cachestats (CLI Component)
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  Andreas Gohr <andi@splitbrain.org>
 */
class cli_plugin_cachestats extends \dokuwiki\Extension\CLIPlugin
{
    /** @inheritDoc */
    protected function setup(Options $options)
    {
        $options->setHelp('Collect statistics about the cache directory.');

        $options->registerOption('noprogress', 'Don\'t show progress dots');
        $options->registerOption('format', 'Output format. Defaults to table.', 'f', 'table|json|csv');
        $options->registerOption('sort', 'Sort by this criteria. Defaults to size.', 's', 'count|size|dups');
    }

    /** @inheritDoc */
    protected function main(Options $options)
    {
        global $conf;

        $sort = $options->getOpt('sort', 'size');
        if (!in_array($sort, ['count', 'size', 'dups'])) {
            $this->error("Invalid sort option '$sort'. Allowed are: count, size, dups.");
            return 1;
        }

        $format = $options->getOpt('format', 'table');
        if (!in_array($format, ['table', 'json', 'csv'])) {
            $this->error("Invalid format option '$format'. Allowed are: table, json, csv.");
            return 1;
        }

        // progress report - collecting stats can take a while
        fprintf(STDERR, 'Collecting cache statistics from ' . $conf['cachedir'] . "…\n");
        if(!$options->getOpt('noprogress')) {
            $cb = function($count) {
                if ($count % 50000 === 0) {
                    fprintf(STDERR, "%s files processed\n", number_format($count));
                } elseif ($count % 1000 === 0) {
                    fprintf(STDERR, ".");
                }
            };
        } else {
            $cb = null;
        }

        $result = (new FileStatistics($conf['cachedir']))->collect($cb);
        if($cb) fprintf(STDERR, "\n");

        // sort with preserved keys
        uasort($result, function ($a, $b) use ($sort) {
            return $b[$sort] <=> $a[$sort];
        });

        match ($format) {
            'json' => $this->print_json($result),
            'csv' => $this->print_csv($result),
            default => $this->print_table($result),
        };
        return 0;
    }

    /**
     * Output statistics as JSON
     */
    private function print_json(array $result): void
    {
        echo json_encode($result, JSON_PRETTY_PRINT);
    }

    /**
     * Output statistics as CSV
     */
    private function print_csv(array $result): void
    {
        $handle = fopen('php://output', 'w');
        if ($handle === false) {
            $this->error('Could not open output for CSV.');
            return;
        }

        $header = array_merge(['extension'], array_keys(reset($result)));
        fputcsv($handle, $header);

        foreach ($result as $ext => $data) {
            fputcsv($handle, array_merge([$ext], $data));
        }
    }

    /**
     * Output statistics as table
     */
    private function print_table(array $result): void
    {
        $colWidth = 9;

        $headers = array_merge(['ext'], array_keys(reset($result)));
        $widths = array_merge(['*'], array_fill(0, count($headers) -1, $colWidth));
        $colors = array_fill(0, count($headers), '');

        // ensure terminal is wide enough, otherwise break ugly
        $tr = new TableFormatter($this->colors);
        if($tr->getMaxWidth() < $colWidth * count($headers)){;
            $tr->setMaxWidth($colWidth * count($headers) + 10);
        }

        echo $tr->format($widths, $headers, $colors);

        foreach ($result as $ext => $data) {
            array_walk(
                $data,
                fn (&$v, $k) => $v = sprintf(
                    "% ${colWidth}s",
                    ($k == 'size') ? filesize_h($v) : number_format($v)
                )
            );

            echo $tr->format(
                $widths,
                array_merge([$ext], array_values($data)),
                $colors
            );
        }
    }
}
