<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Controller;

use DateTime;
use OCA\Mitgliederverwaltung\Db\SavedQuery;
use OCA\Mitgliederverwaltung\Db\SavedQueryMapper;
use OCA\Mitgliederverwaltung\Service\QueryService;
use OCA\Mitgliederverwaltung\Service\ValidationException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

/**
 * REST API controller for saved queries and ad-hoc query execution.
 *
 * Part of Issue #53.
 */
class QueryController extends ApiController {

    use ApiControllerTrait;

    private QueryService $queryService;
    private SavedQueryMapper $savedQueryMapper;
    private LoggerInterface $logger;
    private ?string $userId;

    public function __construct(
        string $appName,
        IRequest $request,
        QueryService $queryService,
        SavedQueryMapper $savedQueryMapper,
        LoggerInterface $logger,
        ?string $userId
    ) {
        parent::__construct($appName, $request);
        $this->queryService = $queryService;
        $this->savedQueryMapper = $savedQueryMapper;
        $this->logger = $logger;
        $this->userId = $userId;
    }

    /**
     * List all saved queries.
     *
     * GET /api/v1/queries
     */
    #[NoCSRFRequired]
    public function index(): JSONResponse {
        try {
            $queries = $this->savedQueryMapper->findAll();
            $data = array_map(fn(SavedQuery $q) => $q->jsonSerialize(), $queries);
            return new JSONResponse(['data' => $data]);
        } catch (\Exception $e) {
            $this->logger->error('Failed to list saved queries', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Create a new saved query.
     *
     * POST /api/v1/queries
     */
    public function create(): JSONResponse {
        try {
            $data = $this->getRequestData();

            if (empty($data['name']) || empty($data['queryJson'])) {
                return new JSONResponse(
                    ['error' => 'name und queryJson sind erforderlich'],
                    Http::STATUS_BAD_REQUEST
                );
            }

            $query = new SavedQuery();
            $query->setName(trim($data['name']));
            $query->setQueryJson(json_encode($data['queryJson']));
            $query->setSortJson(isset($data['sortJson']) ? json_encode($data['sortJson']) : null);
            $query->setCreatedBy($this->userId ?? 'unknown');
            $query->setCreatedAt((new DateTime())->format('Y-m-d H:i:s'));

            /** @var SavedQuery $query */
            $query = $this->savedQueryMapper->insert($query);

            return new JSONResponse($query->jsonSerialize(), Http::STATUS_CREATED);
        } catch (\Exception $e) {
            $this->logger->error('Failed to create saved query', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Update a saved query.
     *
     * PUT /api/v1/queries/{id}
     */
    public function update(int $id): JSONResponse {
        try {
            $query = $this->savedQueryMapper->findById($id);
            $data = $this->getRequestData();

            if (isset($data['name'])) {
                $query->setName(trim($data['name']));
            }
            if (isset($data['queryJson'])) {
                $query->setQueryJson(json_encode($data['queryJson']));
            }
            if (array_key_exists('sortJson', $data)) {
                $query->setSortJson($data['sortJson'] !== null ? json_encode($data['sortJson']) : null);
            }

            /** @var SavedQuery $query */
            $query = $this->savedQueryMapper->update($query);

            return new JSONResponse($query->jsonSerialize());
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Abfrage nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to update saved query', [
                'queryId' => $id,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Delete a saved query.
     *
     * DELETE /api/v1/queries/{id}
     */
    public function destroy(int $id): JSONResponse {
        try {
            $query = $this->savedQueryMapper->findById($id);
            $this->savedQueryMapper->delete($query);
            return new JSONResponse(['status' => 'deleted']);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Abfrage nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to delete saved query', [
                'queryId' => $id,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Execute a saved query.
     *
     * POST /api/v1/queries/{id}/execute
     */
    public function executeSaved(int $id): JSONResponse {
        try {
            $query = $this->savedQueryMapper->findById($id);
            $ast = json_decode($query->getQueryJson(), true);
            $sort = $query->getSortJson() ? json_decode($query->getSortJson(), true) : null;

            $params = $this->getRequestData();
            $limit = isset($params['limit']) ? (int)$params['limit'] : 500;
            $offset = isset($params['offset']) ? (int)$params['offset'] : 0;

            $result = $this->queryService->execute($ast, $sort, $limit, $offset);
            return new JSONResponse($result);
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => 'Abfrage nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_BAD_REQUEST
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to execute saved query', [
                'queryId' => $id,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Execute an ad-hoc query.
     *
     * POST /api/v1/queries/execute
     */
    public function executeAdHoc(): JSONResponse {
        try {
            $data = $this->getRequestData();

            if (empty($data['queryJson'])) {
                return new JSONResponse(
                    ['error' => 'queryJson ist erforderlich'],
                    Http::STATUS_BAD_REQUEST
                );
            }

            $ast = $data['queryJson'];
            $sort = $data['sortJson'] ?? null;
            $limit = isset($data['limit']) ? (int)$data['limit'] : 500;
            $offset = isset($data['offset']) ? (int)$data['offset'] : 0;

            $result = $this->queryService->execute($ast, $sort, $limit, $offset);
            return new JSONResponse($result);
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_BAD_REQUEST
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to execute ad-hoc query', [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Get available query fields metadata.
     *
     * GET /api/v1/queries/fields
     */
    #[NoCSRFRequired]
    public function fields(): JSONResponse {
        return new JSONResponse(['data' => $this->queryService->getAvailableFields()]);
    }

    // getRequestData() provided by ApiControllerTrait
}
