<?php

namespace Espo\Modules\SmartAssistant\Services;

use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\InjectableFactory;
use Espo\ORM\EntityManager;
use Espo\Core\Utils\Log;
use Espo\Core\Utils\Metadata;

class ActionExecutor
{
    private EntityManager $entityManager;
    private InjectableFactory $injectableFactory;
    private Log $log;
    private Metadata $metadata;

    public function __construct(EntityManager $entityManager, InjectableFactory $injectableFactory, Log $log, Metadata $metadata)
    {
        $this->entityManager = $entityManager;
        $this->injectableFactory = $injectableFactory;
        $this->log = $log;
        $this->metadata = $metadata;
    }

    public const VALID_STATUSES = [
        'New' => 'חדש', 'Assigned' => 'בטיפול', 'Pending' => 'ממתין',
        'PendingHearing' => 'ממתין לדיון', 'PendingDecision' => 'ממתין להחלטה',
        'PendingResponse' => 'ממתין לתשובה', 'Negotiation' => 'משא ומתן',
        'Suspended' => 'מושהה', 'ClosedSettlement' => 'סגור - הסכם',
        'ClosedJudgment' => 'סגור - פס״ד', 'Closed' => 'סגור', 'Rejected' => 'נדחה',
    ];

    public function execute(string $tool, array $params, ?string $caseId, string $userId): array
    {
        $this->log->debug("SmartAssistant: Executing tool={$tool} for case={$caseId}");

        return match ($tool) {
            'create_task' => $this->createTask($params, $caseId, $userId),
            'add_note' => $this->addNote($params, $caseId, $userId),
            'change_status' => $this->changeStatus($params, $caseId),
            'create_meeting' => $this->createMeeting($params, $caseId, $userId),
            'create_call' => $this->createCall($params, $caseId, $userId),
            'delete_meeting' => $this->deleteMeeting($params),
            'delete_call' => $this->deleteCall($params),
            'delete_task' => $this->deleteTask($params),
            'delete_note' => $this->deleteNote($params),
            'schedule_hearing' => $this->scheduleHearing($params, $caseId, $userId),
            'list_documents' => $this->listDocuments($caseId),
            'analyze_document' => $this->analyzeDocument($params, $caseId),
            'read_document' => $this->readDocument($params, $caseId),
            'read_multiple_documents' => $this->readMultipleDocuments($params, $caseId),
            'rename_document' => $this->renameDocument($params),
            'batch_rename_documents' => $this->batchRenameDocuments($params),
            'upload_document' => $this->uploadDocument($params, $caseId),
            'generate_document' => $this->generateDocument($params, $caseId),
            'merge_documents' => $this->mergeDocuments($params, $caseId),
            'save_memory' => $this->saveMemory($params, $caseId, $userId),
            'save_rule' => $this->saveRule($params, $userId),
            default => $this->executePluginTool($tool, $params, $caseId, $userId),
        };
    }

    private function saveMemory(array $params, ?string $caseId, string $userId): array
    {
        if (!$caseId) {
            throw new Error('save_memory requires a case context.');
        }

        $service = $this->injectableFactory->create(CaseMemoryService::class);
        $content = $params['content'] ?? '';
        $name = $params['name'] ?? mb_substr($content, 0, 100);

        $entry = $service->createMemory(
            $caseId,
            $params['category'] ?? 'key_facts',
            $name,
            $content,
            $params['importance'] ?? 'normal',
            'assistant'
        );

        return [
            'success' => true,
            'message' => "נשמר בזיכרון התיק: \"{$name}\"",
            'entityType' => 'CaseMemory',
            'entityId' => $entry->get('id'),
        ];
    }

    private function saveRule(array $params, string $userId): array
    {
        $name = $params['name'] ?? '';
        $rule = $params['rule'] ?? '';
        if (!$name || !$rule) {
            throw new Error('save_rule requires name and rule parameters.');
        }

        $entity = $this->entityManager->getNewEntity('AssistantRule');
        $entity->set([
            'name' => $name,
            'rule' => $rule,
            'scope' => $params['scope'] ?? 'global',
            'isActive' => true,
            'createdById' => $userId,
        ]);
        $this->entityManager->saveEntity($entity);

        return [
            'success' => true,
            'message' => "✅ כלל חדש נשמר: \"{$name}\"",
            'entityType' => 'AssistantRule',
            'entityId' => $entity->get('id'),
        ];
    }

    private function createTask(array $params, ?string $caseId, string $userId): array
    {
        $taskData = [
            'name' => $params['name'] ?? 'משימה חדשה',
            'status' => 'Not Started',
            'priority' => $params['priority'] ?? 'Normal',
            'dateEnd' => $this->normalizeDateTime($params['dateEnd'] ?? null),
            'description' => $params['description'] ?? null,
            'assignedUserId' => $params['assignedUserId'] ?? $userId,
        ];

        if ($caseId) {
            $taskData['parentType'] = 'Case';
            $taskData['parentId'] = $caseId;
        }

        $task = $this->entityManager->getNewEntity('Task');
        $task->set($taskData);
        $this->entityManager->saveEntity($task);

        $msg = "✅ משימה \"{$params['name']}\" נוצרה בהצלחה";
        if ($caseId) {
            $case = $this->entityManager->getEntityById('Case', $caseId);
            if ($case) {
                $msg .= " בתיק " . $case->get('name');
            }
        }

        return ['success' => true, 'message' => $msg, 'entityType' => 'Task', 'entityId' => $task->get('id')];
    }

    private function addNote(array $params, ?string $caseId, string $userId): array
    {
        $noteData = ['type' => 'Post', 'post' => $params['post'] ?? '', 'createdById' => $userId];

        if ($caseId) {
            $noteData['parentType'] = 'Case';
            $noteData['parentId'] = $caseId;
        }

        $note = $this->entityManager->getNewEntity('Note');
        $note->set($noteData);
        $this->entityManager->saveEntity($note);
        return ['success' => true, 'message' => '✅ הערה נוספה' . ($caseId ? ' לתיק' : ''), 'entityType' => 'Note', 'entityId' => $note->get('id')];
    }

    private function changeStatus(array $params, ?string $caseId): array
    {
        $case = $this->entityManager->getEntityById('Case', $caseId);
        if (!$case) throw new NotFound("Case {$caseId} not found.");

        $oldStatus = $case->get('status');
        $newStatus = $params['status'] ?? null;
        if (!$newStatus) throw new Error('status parameter is required.');
        if (!isset(self::VALID_STATUSES[$newStatus])) {
            throw new Error("Invalid status '{$newStatus}'. Valid: " . implode(', ', array_keys(self::VALID_STATUSES)));
        }

        $case->set('status', $newStatus);
        $case->set('cLegalStatusManual', true);
        $this->entityManager->saveEntity($case);

        return ['success' => true, 'message' => "סטטוס תיק שונה מ-\"" . (self::VALID_STATUSES[$oldStatus] ?? $oldStatus) . "\" ל-\"" . self::VALID_STATUSES[$newStatus] . "\"", 'entityType' => 'Case', 'entityId' => $caseId];
    }

    private function createMeeting(array $params, ?string $caseId, string $userId): array
    {
        $meetingData = [
            'name' => $params['name'] ?? 'פגישה חדשה', 'status' => 'Planned',
            'dateStart' => $this->normalizeDateTime($params['dateStart'] ?? null),
            'dateEnd' => $this->normalizeDateTime($params['dateEnd'] ?? null),
            'description' => $params['description'] ?? null,
            'assignedUserId' => $userId,
        ];

        if ($caseId) {
            $meetingData['parentType'] = 'Case';
            $meetingData['parentId'] = $caseId;
        }

        $meeting = $this->entityManager->getNewEntity('Meeting');
        $meeting->set($meetingData);
        $this->entityManager->saveEntity($meeting);
        return ['success' => true, 'message' => "✅ פגישה \"{$params['name']}\" נקבעה בהצלחה", 'entityType' => 'Meeting', 'entityId' => $meeting->get('id')];
    }

    private function createCall(array $params, ?string $caseId, string $userId): array
    {
        $callData = [
            'name' => $params['name'] ?? 'שיחה חדשה',
            'status' => $params['status'] ?? 'Held',
            'direction' => $params['direction'] ?? 'Outbound',
            'dateStart' => $this->normalizeDateTime($params['dateStart'] ?? null),
            'dateEnd' => $this->normalizeDateTime($params['dateEnd'] ?? null),
            'description' => $params['description'] ?? null,
            'assignedUserId' => $userId,
        ];

        if ($caseId) {
            $callData['parentType'] = 'Case';
            $callData['parentId'] = $caseId;
        }

        $call = $this->entityManager->getNewEntity('Call');
        $call->set($callData);
        $this->entityManager->saveEntity($call);
        return ['success' => true, 'message' => "✅ שיחה \"{$params['name']}\" תועדה בהצלחה", 'entityType' => 'Call', 'entityId' => $call->get('id')];
    }

    private function deleteMeeting(array $params): array
    {
        $entityId = $params['entityId'] ?? null;
        if (!$entityId) {
            throw new Error('entityId parameter is required.');
        }

        $entity = $this->entityManager->getEntityById('Meeting', $entityId);
        if (!$entity) {
            throw new NotFound("Meeting {$entityId} not found.");
        }

        $name = $entity->get('name') ?? '';
        $this->entityManager->removeEntity($entity);

        return [
            'success' => true,
            'message' => "✅ פגישה \"{$name}\" נמחקה בהצלחה",
            'entityType' => 'Meeting',
            'entityId' => $entityId,
        ];
    }

    private function deleteCall(array $params): array
    {
        $entityId = $params['entityId'] ?? null;
        if (!$entityId) {
            throw new Error('entityId parameter is required.');
        }

        $entity = $this->entityManager->getEntityById('Call', $entityId);
        if (!$entity) {
            throw new NotFound("Call {$entityId} not found.");
        }

        $name = $entity->get('name') ?? '';
        $this->entityManager->removeEntity($entity);

        return [
            'success' => true,
            'message' => "✅ שיחה \"{$name}\" נמחקה בהצלחה",
            'entityType' => 'Call',
            'entityId' => $entityId,
        ];
    }

    private function deleteNote(array $params): array
    {
        $entityId = $params['entityId'] ?? null;
        if (!$entityId) {
            throw new Error('entityId parameter is required.');
        }

        $entity = $this->entityManager->getEntityById('Note', $entityId);
        if (!$entity) {
            throw new NotFound("Note {$entityId} not found.");
        }

        $this->entityManager->removeEntity($entity);

        return [
            'success' => true,
            'message' => '✅ הערה נמחקה בהצלחה',
            'entityType' => 'Note',
            'entityId' => $entityId,
        ];
    }

    private function deleteTask(array $params): array
    {
        $entityId = $params['entityId'] ?? null;
        if (!$entityId) {
            throw new Error('entityId parameter is required.');
        }

        $entity = $this->entityManager->getEntityById('Task', $entityId);
        if (!$entity) {
            throw new NotFound("Task {$entityId} not found.");
        }

        $name = $entity->get('name') ?? '';
        $this->entityManager->removeEntity($entity);

        return [
            'success' => true,
            'message' => "✅ משימה \"{$name}\" נמחקה בהצלחה",
            'entityType' => 'Task',
            'entityId' => $entityId,
        ];
    }

    private function scheduleHearing(array $params, ?string $caseId, string $userId): array
    {
        $case = $this->entityManager->getEntityById('Case', $caseId);
        if (!$case) throw new NotFound("Case {$caseId} not found.");

        $dateTime = $this->normalizeDateTime($params['dateTime'] ?? null);
        if ($dateTime) {
            $case->set('cNextHearing', $dateTime);
            $this->entityManager->saveEntity($case);
        }

        $meetingName = $params['description'] ?? 'דיון בבית המשפט';
        if (!empty($params['court'])) $meetingName .= ' - ' . $params['court'];
        $dateEnd = $dateTime ? date('Y-m-d H:i:s', strtotime($dateTime) + 3600) : null;

        $meeting = $this->entityManager->getNewEntity('Meeting');
        $meeting->set([
            'name' => $meetingName, 'status' => 'Planned',
            'dateStart' => $dateTime, 'dateEnd' => $dateEnd,
            'description' => $params['description'] ?? null,
            'parentType' => 'Case', 'parentId' => $caseId, 'assignedUserId' => $userId,
        ]);
        $this->entityManager->saveEntity($meeting);
        return ['success' => true, 'message' => "דיון נקבע ל-{$dateTime}", 'entityType' => 'Meeting', 'entityId' => $meeting->get('id')];
    }

    private function listDocuments(?string $caseId): array
    {
        if (!$caseId) return ['success' => true, 'message' => 'לא ניתן להציג מסמכים ללא תיק'];

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $result = $analyzer->listDocumentsRecursive($caseId);

        if ($result['totalFiles'] === 0) return ['success' => true, 'message' => 'לא נמצאו מסמכים בתיק'];

        $lines = ["נמצאו {$result['totalFiles']} מסמכים בתיק:", ''];
        foreach ($result['folders'] as $folder) {
            $lines[] = "📁 {$folder['name']} (" . count($folder['files']) . " קבצים)";
            foreach ($folder['files'] as $file) {
                $lines[] = "   - {$file['name']} (" . $this->formatFileSize($file['size']) . ")";
            }
        }
        if (!empty($result['rootFiles'])) {
            $lines[] = '';
            $lines[] = "📄 קבצים בתיקייה הראשית:";
            foreach ($result['rootFiles'] as $file) {
                $lines[] = "   - {$file['name']} (" . $this->formatFileSize($file['size']) . ")";
            }
        }

        return ['success' => true, 'message' => implode("\n", $lines), 'data' => $result];
    }

    private function analyzeDocument(array $params, ?string $caseId): array
    {
        $filePath = $params['filePath'] ?? null;
        if (!$filePath) throw new Error('filePath parameter is required.');

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $lines = ["ניתוח מסמך: " . basename($filePath)];
        $text = $analyzer->extractTextContent($filePath);

        if ($text) {
            $lines = array_merge($lines, ['', 'תוכן מתוך המסמך:', '---', $text, '---', '', 'ניתן להשתמש בכלי rename_document כדי לשנות את שם הקובץ.']);
        } else {
            $lines[] = 'לא ניתן לחלץ טקסט מהמסמך';
        }

        return ['success' => true, 'message' => implode("\n", $lines), 'data' => ['filePath' => $filePath, 'hasTextContent' => $text !== null]];
    }

    private function readDocument(array $params, ?string $caseId): array
    {
        $filePath = $params['filePath'] ?? null;
        if (!$filePath) throw new Error('filePath parameter is required.');

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

        if ($text === null) {
            return ['success' => false, 'message' => "לא ניתן לחלץ טקסט מ-\"{$fileName}\""];
        }

        return [
            'success' => true,
            'message' => $text,
            'data' => [
                'filePath' => $filePath,
                'fileName' => $fileName,
                'charCount' => mb_strlen($text),
            ],
        ];
    }

    private function readMultipleDocuments(array $params, ?string $caseId): array
    {
        $filePaths = $params['filePaths'] ?? [];
        if (empty($filePaths)) throw new Error('filePaths parameter is required (array of file paths).');

        $maxPerFile = $params['maxCharsPerFile'] ?? 50000;
        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $results = $analyzer->extractMultipleDocuments($filePaths, $maxPerFile);

        $lines = [];
        $successCount = 0;

        foreach ($results as $r) {
            $lines[] = "=== {$r['fileName']} ===";
            if (!empty($r['error'])) {
                $lines[] = "שגיאה: {$r['error']}";
            } elseif ($r['text'] === null) {
                $lines[] = "לא ניתן לחלץ טקסט";
            } else {
                $lines[] = $r['text'];
                $successCount++;
            }
            $lines[] = '';
        }

        return [
            'success' => true,
            'message' => implode("\n", $lines),
            'data' => [
                'totalFiles' => count($filePaths),
                'successCount' => $successCount,
            ],
        ];
    }

    private function renameDocument(array $params): array
    {
        $sourcePath = $params['sourcePath'] ?? null;
        $newName = $params['newName'] ?? null;
        if (!$sourcePath) throw new Error('sourcePath parameter is required.');
        if (!$newName) throw new Error('newName parameter is required.');

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $result = $analyzer->renameDocument($sourcePath, $newName);
        return ['success' => true, 'message' => "המסמך שונה מ-\"{$result['oldName']}\" ל-\"{$result['newName']}\"", 'data' => $result];
    }

    private function batchRenameDocuments(array $params): array
    {
        $renames = $params['renames'] ?? [];
        if (empty($renames)) throw new Error('renames parameter is required (array of {sourcePath, newName}).');

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

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

            if (!$sourcePath || !$newName) {
                $results[] = "שגיאה: חסר sourcePath או newName";
                continue;
            }

            try {
                $result = $analyzer->renameDocument($sourcePath, $newName);
                $results[] = "\"{$result['oldName']}\" → \"{$result['newName']}\"";
                $successCount++;
            } catch (\Exception $e) {
                $results[] = "שגיאה בשינוי שם \"" . basename($sourcePath) . "\": " . $e->getMessage();
            }
        }

        $msg = "שינוי שמות: {$successCount}/" . count($renames) . " הצליחו\n" . implode("\n", $results);
        return ['success' => true, 'message' => $msg];
    }

    private function uploadDocument(array $params, ?string $caseId): array
    {
        if (!$caseId) {
            throw new Error('upload_document requires a case context.');
        }

        $fileName = $params['fileName'] ?? null;
        $content = $params['content'] ?? null;
        if (!$fileName) throw new Error('fileName parameter is required.');
        if (!$content) throw new Error('content parameter is required (base64-encoded file data).');

        $decoded = base64_decode($content, true);
        if ($decoded === false) {
            throw new Error('content is not valid base64.');
        }

        $analyzer = $this->injectableFactory->create(DocumentAnalyzer::class);
        $result = $analyzer->uploadDocument($caseId, $fileName, $decoded, $params['folder'] ?? null);

        $msg = "✅ מסמך \"{$fileName}\" הועלה בהצלחה";
        if (!empty($params['folder'])) {
            $msg .= " לתיקייה \"{$params['folder']}\"";
        }

        return ['success' => true, 'message' => $msg, 'data' => $result];
    }

    private function generateDocument(array $params, ?string $caseId): array
    {
        if (!$caseId) {
            throw new Error('generate_document requires a case context.');
        }

        $templateId = $params['templateId'] ?? null;
        if (!$templateId) throw new Error('templateId parameter is required.');

        $template = $this->entityManager->getEntityById('DocumentTemplate', $templateId);
        if (!$template) throw new NotFound("Template {$templateId} not found.");

        $documentSubject = $params['documentSubject'] ?? null;

        $templateService = $this->injectableFactory->create(
            \Espo\Modules\NetworkStorageIntegration\Services\DocumentTemplateService::class
        );

        $result = $templateService->createFromTemplate($templateId, 'Case', $caseId, $documentSubject);

        if (empty($result['success'])) {
            throw new Error('Failed to generate document from template.');
        }

        $templateName = $template->get('name') ?? 'document';
        $fileName = $result['fileName'] ?? "{$templateName}.docx";

        $msg = "✅ מסמך \"{$fileName}\" נוצר בהצלחה מתבנית \"{$templateName}\"";

        return [
            'success' => true,
            'message' => $msg,
            'data' => ['fileName' => $fileName],
        ];
    }

    private function mergeDocuments(array $params, ?string $caseId): array
    {
        if (!$caseId) {
            throw new Error('merge_documents requires a case context.');
        }

        $documentIds = $params['documentIds'] ?? [];
        if (empty($documentIds)) throw new Error('documentIds parameter is required (array of document IDs).');

        $documents = [];
        foreach ($documentIds as $docId) {
            $doc = $this->entityManager->getEntityById('Document', $docId);
            if (!$doc) throw new NotFound("Document {$docId} not found.");
            $documents[] = $doc;
        }

        $mergerService = $this->injectableFactory->create(
            \Espo\Modules\NetworkStorageIntegration\Services\DocumentMergerService::class
        );

        $options = $params['options'] ?? [];
        $mergedPath = $mergerService->mergeWithLabels($documents, $options);

        return [
            'success' => true,
            'message' => "✅ " . count($documents) . " מסמכים מוזגו בהצלחה",
            'data' => ['path' => $mergedPath, 'documentCount' => count($documents)],
        ];
    }

    private function normalizeDateTime(?string $value): ?string
    {
        if ($value === null || $value === '') return null;
        $ts = strtotime($value);
        return $ts === false ? null : date('Y-m-d H:i:s', $ts);
    }

    private function formatFileSize(?int $bytes): string
    {
        if ($bytes === null) return '?';
        if ($bytes >= 1048576) return round($bytes / 1048576, 1) . ' MB';
        if ($bytes >= 1024) return round($bytes / 1024, 1) . ' KB';
        return $bytes . ' B';
    }

    private function executePluginTool(string $tool, array $params, ?string $caseId, string $userId): array
    {
        $handlerClass = $this->metadata->get(['app', 'smartAssistant', 'tools', $tool, 'handler']);

        if (!$handlerClass) {
            throw new Error("Unknown tool: {$tool}");
        }

        $this->log->debug("SmartAssistant: Executing plugin tool={$tool} handler={$handlerClass}");

        $handler = $this->injectableFactory->create($handlerClass);

        if (!method_exists($handler, 'execute')) {
            throw new Error("Plugin tool handler {$handlerClass} must have an execute() method.");
        }

        return $handler->execute($params, $caseId, $userId);
    }
}
