<?php

namespace Espo\Modules\SmartAssistant\Controllers;

use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\InjectableFactory;
use Espo\Core\Acl;
use Espo\Modules\SmartAssistant\Services\SmartAssistantService;
use Espo\Modules\SmartAssistant\Services\ActionExecutor;
use Espo\Modules\SmartAssistant\Services\CaseMemoryService;
use Espo\Modules\SmartAssistant\Services\DocumentAnalyzer;
use Espo\Entities\User;

class SmartAssistant
{
    private InjectableFactory $injectableFactory;
    private Acl $acl;
    private User $user;

    public function __construct(InjectableFactory $injectableFactory, Acl $acl, User $user)
    {
        $this->injectableFactory = $injectableFactory;
        $this->acl = $acl;
        $this->user = $user;
    }

    private function getService(): SmartAssistantService
    {
        return $this->injectableFactory->create(SmartAssistantService::class);
    }

    private function checkAccess(): void
    {
        if (!$this->acl->checkScope('Case', 'read')) {
            throw new Forbidden('No access to Case.');
        }
    }

    public function getActionStatus(Request $request, Response $response): array
    {
        return $this->getService()->getStatus();
    }

    public function postActionChat(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        if (empty($data->message)) {
            throw new BadRequest('message is required.');
        }

        $mode = $data->mode ?? 'office';
        $caseId = $data->caseId ?? null;

        if ($mode === 'case' && empty($caseId)) {
            throw new BadRequest('caseId is required for case mode.');
        }

        return $this->getService()->chat(
            trim($data->message),
            $mode,
            $caseId,
            $data->conversationId ?? null
        );
    }

    public function postActionExecute(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        if (empty($data->conversationId) || empty($data->actionId)) {
            throw new BadRequest('conversationId and actionId are required.');
        }

        return $this->getService()->executeAction(
            $data->conversationId,
            $data->actionId,
            (bool) ($data->approved ?? false)
        );
    }

    public function getActionSummary(Request $request, Response $response): array
    {
        $this->checkAccess();
        return $this->getService()->getSummary();
    }

    public function getActionAlerts(Request $request, Response $response): array
    {
        $this->checkAccess();
        return $this->getService()->getAlerts();
    }

    public function getActionHistory(Request $request, Response $response): array
    {
        $this->checkAccess();
        $caseId = $request->getQueryParam('caseId');
        return $this->getService()->getHistory($caseId);
    }

    public function getActionConversations(Request $request, Response $response): array
    {
        $this->checkAccess();
        $type = $request->getQueryParam('type');
        return $this->getService()->getConversations($type);
    }

    public function getActionConversationMessages(Request $request, Response $response): array
    {
        $this->checkAccess();
        $key = $request->getQueryParam('conversationKey');

        if (empty($key)) {
            throw new BadRequest('conversationKey is required.');
        }

        return $this->getService()->getConversationMessages($key);
    }

    public function getActionSearchHistory(Request $request, Response $response): array
    {
        $this->checkAccess();
        $query = $request->getQueryParam('query');

        if (empty($query)) {
            throw new BadRequest('query is required.');
        }

        return $this->getService()->searchHistory($query);
    }

    public function getActionCaseMemory(Request $request, Response $response): array
    {
        $this->checkAccess();
        $caseId = $request->getQueryParam('caseId');

        if (empty($caseId)) {
            throw new BadRequest('caseId is required.');
        }

        $category = $request->getQueryParam('category');
        $includeArchived = (bool) $request->getQueryParam('includeArchived');

        $service = $this->injectableFactory->create(CaseMemoryService::class);
        return $service->getMemoriesByCase($caseId, $category, $includeArchived);
    }

    public function postActionSaveMemory(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        if (empty($data->caseId) || empty($data->content)) {
            throw new BadRequest('caseId and content are required.');
        }

        $service = $this->injectableFactory->create(CaseMemoryService::class);
        $entry = $service->createMemory(
            $data->caseId,
            $data->category ?? 'key_facts',
            $data->name ?? mb_substr($data->content, 0, 100),
            $data->content,
            $data->importance ?? 'normal',
            'manual'
        );

        return [
            'success' => true,
            'id' => $entry->get('id'),
        ];
    }

    public function postActionExecuteTool(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        if (empty($data->tool)) {
            throw new BadRequest('tool is required.');
        }

        $tool = $data->tool;
        $params = (array) ($data->params ?? []);
        $caseId = $data->caseId ?? null;

        $executor = $this->injectableFactory->create(ActionExecutor::class);

        return $executor->execute($tool, $params, $caseId, $this->user->getId());
    }

    public function postActionReadDocument(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        $filePath = $data->filePath ?? null;
        if (empty($filePath)) {
            throw new BadRequest('filePath is required.');
        }

        $maxLength = isset($data->maxLength) ? (int) $data->maxLength : null;

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $text = $analyzer->extractFullText($filePath, $maxLength);

        return [
            'success' => $text !== null,
            'text' => $text,
            'fileName' => basename($filePath),
            'charCount' => $text !== null ? mb_strlen($text) : 0,
        ];
    }

    public function postActionReadMultipleDocuments(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        $filePaths = $data->filePaths ?? [];
        if (empty($filePaths)) {
            throw new BadRequest('filePaths is required (array of file paths).');
        }

        $maxPerFile = isset($data->maxCharsPerFile) ? (int) $data->maxCharsPerFile : 50000;

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $results = $analyzer->extractMultipleDocuments((array) $filePaths, $maxPerFile);

        return [
            'success' => true,
            'documents' => $results,
        ];
    }

    public function postActionBatchRename(Request $request, Response $response): array
    {
        $this->checkAccess();
        $data = $request->getParsedBody();

        $renames = $data->renames ?? [];
        if (empty($renames)) {
            throw new BadRequest('renames is required (array of {sourcePath, newName}).');
        }

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $results = [];
        $successCount = 0;

        foreach ($renames as $rename) {
            $rename = (array) $rename;
            $sourcePath = $rename['sourcePath'] ?? null;
            $newName = $rename['newName'] ?? null;

            if (!$sourcePath || !$newName) {
                $results[] = ['success' => false, 'error' => 'Missing sourcePath or newName'];
                continue;
            }

            try {
                $result = $analyzer->renameDocument($sourcePath, $newName);
                $results[] = ['success' => true, 'oldName' => $result['oldName'], 'newName' => $result['newName']];
                $successCount++;
            } catch (\Exception $e) {
                $results[] = ['success' => false, 'oldName' => basename($sourcePath), 'error' => $e->getMessage()];
            }
        }

        return [
            'success' => true,
            'totalCount' => count($renames),
            'successCount' => $successCount,
            'results' => $results,
        ];
    }
}
