<?php

declare(strict_types=1);

namespace PrettyLinks\Pro\Rest;

use PrettyLinks\Pro\Reports\SplitTest as SplitTestReport;
use PrettyLinks\Pro\Reports\SplitTestData;
use PrettyLinks\Pro\Tools\AggregateCsvExporter;
use PrettyLinks\Pro\Tools\SplitTestCsvExporter;
use PrettyLinks\Repositories\LinkMetas;
use PrettyLinks\Repositories\Links as LinksRepo;
use PrettyLinks\Rest\Controllers\BaseController;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Split-test report endpoint at `/pro/split-test/{link_id}`.
 *
 * Composes:
 *  - `SplitTest::summaryByVariant()`   — clicks + uniques per variant
 *  - `SplitTest::timeseriesByVariant()` — per-variant chart series
 *  - `SplitTest::currentVariants()`     — current rotation config (so the UI
 *      can flag "(removed)" for historical-only variants)
 *  - `Conversions::byVariant()`         — per-variant conversion counts when
 *      a goal link is configured
 *
 * Response shape is consumed by `pro/src-js/split-test-report/index.js`.
 * When split testing is disabled for the link the endpoint still responds
 * 200 with `splitTestEnabled: false` so the UI can render the disabled
 * state instead of a generic error.
 */
class SplitTestController extends BaseController
{
    /**
     * Register the split-test REST route.
     *
     * @return void
     */
    public function register(): void
    {
        register_rest_route($this->namespace(), '/pro/split-test/(?P<link_id>\d+)', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'show'],
                'permission_callback' => $this->permission(),
                'args'                => [
                    'link_id'     => [
                        'type'     => 'integer',
                        'required' => true,
                    ],
                    'from'        => ['type' => 'string'],
                    'to'          => ['type' => 'string'],
                    'granularity' => [
                        'type'    => 'string',
                        'enum'    => ['day', 'hour'],
                        'default' => 'day',
                    ],
                ],
            ],
        ]);

        register_rest_route($this->namespace(), '/pro/split-test/(?P<link_id>\d+)/export', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [$this, 'export'],
                'permission_callback' => $this->permission(),
                'args'                => [
                    'link_id'    => [
                        'type'     => 'integer',
                        'required' => true,
                    ],
                    'from'       => ['type' => 'string'],
                    'to'         => ['type' => 'string'],
                    'offset'     => ['type' => 'integer'],
                    'limit'      => ['type' => 'integer'],
                    'count_only' => ['type' => 'boolean'],
                ],
            ],
        ]);
    }

    /**
     * Return split-test report data for a link.
     *
     * @param WP_REST_Request $request Incoming REST request.
     *
     * @return WP_REST_Response
     */
    public function show(WP_REST_Request $request): WP_REST_Response
    {
        $linkId = (int) $request['link_id'];
        $gran   = (string) $request->get_param('granularity');
        if ($gran !== 'hour') {
            $gran = 'day';
        }

        $payload = $this->splitTestData()->variants(
            $linkId,
            (string) $request->get_param('from'),
            (string) $request->get_param('to')
        );
        if ($payload === null) {
            return new WP_REST_Response(['error' => 'not_found'], 404);
        }

        $payload['granularity'] = $gran;

        // Layer the per-variant time-series onto the composed rows. Only
        // needed for the JSON chart view — the CSV export skips it.
        if (!empty($payload['splitTestEnabled']) && $payload['variants'] !== []) {
            $report = new SplitTestReport($this->linksRepo(), $this->linkMetas());
            $series = $report->timeseriesByVariant(
                $linkId,
                (string) $payload['from'],
                (string) $payload['to'],
                $gran
            );
            $seriesByUrl = [];
            foreach ($series as $row) {
                $seriesByUrl[$row['url']] = $row['series'];
            }
            foreach ($payload['variants'] as &$variant) {
                $variant['series'] = $seriesByUrl[$variant['url']] ?? [];
            }
            unset($variant);
        }

        return new WP_REST_Response($payload);
    }

    /**
     * Export a link's split-test variant data as CSV.
     *
     * Honors the shared chunk contract consumed by
     * `src-js/shared/csv-download.js`: `count_only` returns a row count,
     * `offset`/`limit` return a chunk, otherwise a single-shot `csv`.
     *
     * @param WP_REST_Request $request Incoming REST request.
     *
     * @return WP_REST_Response|WP_Error
     */
    public function export(WP_REST_Request $request)
    {
        $linkId = (int) $request['link_id'];
        if ($this->linksRepo()->find($linkId) === null) {
            return new WP_Error('not_found', __('Link not found.', 'pretty-link-pro'), ['status' => 404]);
        }

        $args = [
            'link_id' => $linkId,
            'from'    => (string) $request->get_param('from'),
            'to'      => (string) $request->get_param('to'),
        ];

        $exporter = new SplitTestCsvExporter($this->splitTestData());

        if ($request->get_param('count_only')) {
            return new WP_REST_Response(['total' => $exporter->total($args)]);
        }

        $offsetParam = $request->get_param('offset');
        if ($offsetParam !== null && $offsetParam !== '') {
            $offset = (int) $offsetParam;
            $limit  = (int) ($request->get_param('limit') ?: AggregateCsvExporter::DEFAULT_CHUNK_SIZE);
            return new WP_REST_Response($exporter->chunk($args, $offset, $limit));
        }

        return new WP_REST_Response(['csv' => $exporter->fullCsv($args)]);
    }

    /**
     * Build the split-test data composer, wired with the repositories.
     *
     * @return SplitTestData
     */
    private function splitTestData(): SplitTestData
    {
        return new SplitTestData($this->linksRepo(), $this->linkMetas());
    }

    /**
     * Resolve the links repository from the container.
     *
     * @return LinksRepo
     */
    private function linksRepo(): LinksRepo
    {
        return $this->container->get(LinksRepo::class);
    }

    /**
     * Resolve the link-metas repository from the container.
     *
     * @return LinkMetas
     */
    private function linkMetas(): LinkMetas
    {
        return $this->container->get(LinkMetas::class);
    }
}
