<?php

declare(strict_types=1);

namespace Maestro\Core\Http\Controllers\Api\DataTables;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Maestro\Core\DataTables\DataTableColumnCatalog;
use Maestro\Core\DataTables\DataTableColumnDefaultsMerger;
use Maestro\Core\DataTables\DataTableQueryApplier;
use Maestro\Core\DataTables\DataTableRegistry;
use Maestro\Core\DataTables\ExtraDatatableColumnDefinitions;
use Maestro\Core\Http\Controllers\Controller;
use Maestro\Core\Http\Requests\DataTables\ExportDataTableRequest;
use Maestro\Core\Http\Requests\DataTables\FetchDataTableRequest;
use Maestro\Core\Jobs\ExportDataTableJob;
use Maestro\Core\Models\DataTableExport;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

use function activity;

final class DataTableController extends Controller
{
    public function __construct(
        private readonly DataTableRegistry $registry,
        private readonly DataTableColumnCatalog $columnCatalog,
        private readonly DataTableQueryApplier $applier,
        private readonly DataTableColumnDefaultsMerger $columnDefaultsMerger,
    ) {}

    public function columns(Request $request, string $resourceKey): JsonResponse
    {
        if (! $this->columnCatalog->exists($resourceKey)) {
            abort(404, 'Unknown resource key.');
        }

        try {
            $this->registry->resolve($resourceKey)->authorize($request);
        } catch (NotFoundHttpException) {
            ExtraDatatableColumnDefinitions::authorize($request, $resourceKey);
        }

        $defs = $this->columnCatalog->definitions($resourceKey);
        $out = $this->columnDefaultsMerger->buildApiColumns($resourceKey, $defs);

        return response()->json($out);
    }

    public function fetch(FetchDataTableRequest $request, string $resourceKey): JsonResponse
    {
        $resource = $this->registry->resolve($resourceKey);
        $resource->authorize($request);

        $query = $resource->baseQuery($request);
        $columns = $resource->columns();
        $payload = $request->validated();

        $countQuery = clone $query;
        $base = $countQuery->getQuery();
        $base->groups = null;
        $model = $countQuery->getModel();
        $idCol = $model ? $model->getTable().'.id' : 'id';
        $totalAll = $countQuery->distinct($idCol)->count($idCol);

        $result = $this->applier->apply(
            $query,
            $columns,
            $payload,
            fn (object $model) => $resource->transformRow($model),
            $resource,
        );

        $result['meta']['resourceKey'] = $resource->key();
        $result['meta']['totalAll'] = $totalAll;

        return response()->json($result);
    }

    public function export(ExportDataTableRequest $request, string $resourceKey): JsonResponse
    {
        $resource = $this->registry->resolve($resourceKey);
        $resource->authorize($request);

        $resourceLabel = trans('maestro::datatable.resources.'.$resourceKey);
        if ($resourceLabel === 'maestro::datatable.resources.'.$resourceKey) {
            $resourceLabel = $resourceKey;
        }

        $payload = $request->validated();
        $columns = $resource->columns();
        $exportColumns = $this->applier->orderedExportColumns($columns, $payload);
        if ($exportColumns === []) {
            return response()->json(['message' => 'No valid columns to export.'], 422);
        }

        $export = DataTableExport::create([
            'user_id' => $request->user()->id,
            'resource_key' => $resourceKey,
            'payload' => $payload,
            'status' => DataTableExport::STATUS_PENDING,
        ]);

        ExportDataTableJob::dispatch($export->id)->onQueue('exports');

        activity('data_export')
            ->causedBy($request->user())
            ->event('export')
            ->withProperties([
                'resource_key' => $resourceKey,
                'export_id' => $export->id,
                'status' => DataTableExport::STATUS_PENDING,
            ])
            ->log('Data export to CSV started for: '.$resourceKey);

        return response()->json([
            'id' => $export->id,
            'status' => $export->status,
            'resourceKey' => $resourceKey,
            'resourceLabel' => $resourceLabel,
            'pollingIntervalHintMs' => 3000,
        ], 201);
    }
}
