<?php

namespace Mtc\Plugins\Agent\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Log;
use Mtc\Plugins\Agent\Agents\PharmacyAgent;
use Mtc\Plugins\Agent\Models\AgentSettings;
use Mtc\Plugins\Agent\Models\AgentSettingsBackground;
use Mtc\Plugins\Agent\Models\AgentSettingsOutput;
use Mtc\Plugins\Agent\Models\AgentSettingsSteps;
use Mtc\Plugins\Agent\Models\TokenUsage;
use NeuronAI\RAG\DataLoader\FileDataLoader;
use NeuronAI\RAG\DataLoader\PdfReader;
use NeuronAI\RAG\Splitter\SentenceTextSplitter;

class AgentSettingsController extends Controller
{
    protected AgentSettings $settings;

    public function __construct()
    {
        $this->settings = new AgentSettings();
    }

    public function getPosition() : JsonResponse
    {
        $settings = $this->settings->first();
        return response()->json([
            'position' => $settings->position ?? 'bottom-right',
        ]);
    }

    public function getSettings() : JsonResponse
    {
        $settings = $this->settings->first();
        $data = $settings ? $settings->toArray() : [];

        $data['backgrounds'] = AgentSettingsBackground::orderBy('order')->get();
        $data['steps'] = AgentSettingsSteps::orderBy('order')->get();
        $data['outputs'] = AgentSettingsOutput::orderBy('order')->get();
        $data['has_openai_key'] = $settings && !empty($settings->openai_api_key);
        $data['has_pinecone_key'] = $settings && !empty($settings->pinecone_api_key);

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

    public function updateSettings(Request $request) : JsonResponse
    {
        $validated = $request->validate([
            'title'                  => 'sometimes|required|string|max:100',
            'subtitle'               => 'sometimes|nullable|string|max:255',
            'first_message'          => 'sometimes|nullable|string|max:500',
            'input_max_tokens'       => 'sometimes|nullable|integer|min:0',
            'output_max_tokens'      => 'sometimes|nullable|integer|min:0',
            'background'             => 'sometimes|nullable|string|max:255',
            'position'               => 'sometimes|nullable|string|in:top-left,top-right,bottom-left,bottom-right',
            'steps'                  => 'sometimes|nullable|string|max:255',
            'output'                 => 'sometimes|nullable|string|max:255',
            'openai_api_key'         => 'sometimes|nullable|string|max:255',
            'openai_model'           => 'sometimes|nullable|string|max:50',
            'openai_embedding_model' => 'sometimes|nullable|string|max:50',
            'pinecone_api_key'       => 'sometimes|nullable|string|max:255',
            'pinecone_index_url'     => 'sometimes|nullable|url|max:255',
        ]);

        foreach (['openai_api_key', 'pinecone_api_key'] as $secret) {
            if (array_key_exists($secret, $validated) && $validated[$secret] === '') {
                unset($validated[$secret]);
            }
        }

        try {
            AgentSettings::updateOrCreate(
                ['id' => 1],
                $validated
            );

            return response()->json(['status' => 'Settings updated successfully']);
        }
        catch (\Exception $e) {
            Log::error("Error updating settings: {$e->getMessage()}");
            return response()->json(['status' => 'Settings update failed.'], 500);
        }
    }

    public function getTokenUsage() : bool
    {
        try {
            $today = date('Y-m-d');

            $usage = TokenUsage::where('date', $today)
                               ->first();

            if ($usage) {
                $settings = AgentSettings::first();

                $usageInput = $usage->input_tokens;
                $usageOutput = $usage->output_tokens;

                $limitInput = $settings->input_max_tokens ?? 0;
                $limitOutput = $settings->output_max_tokens ?? 0;

                if ($limitInput < $usageInput || $limitOutput < $usageOutput) {
                    return false;
                }
                else {
                    return true;
                }
            }
            else {
                return true;
            }
        }
        catch (\Exception $e) {
            Log::error("Error fetching token usage: {$e->getMessage()}");
            return false;
        }
    }

    public function getTokenDailyUsage() : JsonResponse
    {
        try {
            $access = $this->getTokenUsage();
            return response()->json(['access' => $access]);
        }
        catch (\Exception $e) {
            Log::error("Error fetching token usage: {$e->getMessage()}");
            return response()->json(['access' => false]);
        }
    }


    private function swapOrder(string $modelClass, int $id1, int $id2): void
    {
        $item1 = $modelClass::findOrFail($id1);
        $item2 = $modelClass::findOrFail($id2);

        $tempOrder = $item1->order;
        $item1->order = $item2->order;
        $item2->order = $tempOrder;

        $item1->save();
        $item2->save();
    }

    public function swapOrderSteps(Request $request): JsonResponse
    {
        try {
            $data = $request->only(['order1', 'order2']);
            $this->swapOrder(AgentSettingsSteps::class, $data['order1'], $data['order2']);
            return response()->json(['message' => true]);
        }
        catch (\Exception $e) {
            Log::error("Error changing order steps: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function addSteps(Request $request): JsonResponse
    {
        try {
            $data = $request->only(['step', 'order']);
            $step = AgentSettingsSteps::create($data);
            return response()->json(['message' => true, 'data' => $step]);
        } catch (\Exception $e) {
            Log::error("Error adding step: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function removeSteps(int $id): JsonResponse
    {
        try {
            AgentSettingsSteps::findOrFail($id)->delete();
            return response()->json(['message' => true]);
        } catch (\Exception $e) {
            Log::error("Error removing step: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function updateSteps(Request $request, int $id): JsonResponse
    {
        try {
            $data = $request->only(['step', 'order']);
            $step = AgentSettingsSteps::findOrFail($id);
            $step->update($data);
            return response()->json(['message' => true, 'data' => $step]);
        } catch (\Exception $e) {
            Log::error("Error updating step: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function swapOrderBackground(Request $request): JsonResponse
    {
        try {
            $data = $request->only(['order1', 'order2']);
            $this->swapOrder(AgentSettingsBackground::class, $data['order1'], $data['order2']);
            return response()->json(['message' => true]);
        }
        catch (\Exception $e) {
            Log::error("Error changing order for model agent settings: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function addBackground(Request $request): JsonResponse
    {
        try {
            $data = $request->only(['background', 'order']);
            $background = AgentSettingsBackground::create($data);
            return response()->json(['message' => true, 'data' => $background]);
        } catch (\Exception $e) {
            Log::error("Error adding background: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function removeBackground(int $id): JsonResponse
    {
        try {
            AgentSettingsBackground::findOrFail($id)->delete();
            return response()->json(['message' => true]);
        } catch (\Exception $e) {
            Log::error("Error removing background: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function updateBackground(Request $request, int $id): JsonResponse
    {
        try {
            $data = $request->only(['background', 'order']);
            $background = AgentSettingsBackground::findOrFail($id);
            $background->update($data);
            return response()->json(['message' => true, 'data' => $background]);
        } catch (\Exception $e) {
            Log::error("Error updating background: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function swapOrderOutput(Request $request): JsonResponse
    {
        try {
            $data = $request->only(['order1', 'order2']);
            $this->swapOrder(AgentSettingsOutput::class, $data['order1'], $data['order2']);
            return response()->json(['message' => true]);
        }
        catch (\Exception $e) {
            Log::error("Error changing order for output: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function addOutput(Request $request): JsonResponse
    {
        try {
            $data = $request->only(['output', 'order']);
            $output = AgentSettingsOutput::create($data);
            return response()->json(['message' => true, 'data' => $output]);
        } catch (\Exception $e) {
            Log::error("Error adding output: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function removeOutput(int $id): JsonResponse
    {
        try {
            AgentSettingsOutput::findOrFail($id)->delete();
            return response()->json(['message' => true]);
        } catch (\Exception $e) {
            Log::error("Error removing output: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    public function updateOutput(Request $request, int $id): JsonResponse
    {
        try {
            $data = $request->only(['output', 'order']);
            $output = AgentSettingsOutput::findOrFail($id);
            $output->update($data);
            return response()->json(['message' => true, 'data' => $output]);
        } catch (\Exception $e) {
            Log::error("Error updating output: {$e->getMessage()}");
            return response()->json(['message' => false]);
        }
    }

    private function fileVectorize(string $filePath, string $fileName, string $fileSize, string $fileType) : JsonResponse
    {
        try {
            $docs = FileDataLoader::for($filePath)
                ->addReader('pdf', new PdfReader())
                ->withSplitter(new SentenceTextSplitter(maxWords: 200, overlapWords: 25))
                ->getDocuments();

            foreach ($docs as $doc) {
                $doc->addMetadata('file_name', $fileName);
                $doc->addMetadata('file_size', $fileSize);
                $doc->addMetadata('file_type', $fileType);
            }

            Log::debug("Vectorizing documents from uploaded file");

            PharmacyAgent::make()->addDocuments($docs);
            return response()->json(['message' => true]);
        }
        catch (\Exception $e) {
            Log::error("Error adding file: {$e->getMessage()}");
            return response()->json(['message' => 'Error vectorizing']);
        }

    }

    public function addContext(Request $request) : JsonResponse
    {

        try {
            $request->validate([
                'file' => 'required|file|mimes:pdf,txt|max:10240', // max 10MB
            ]);

            $file = $request->file('file');

            // Store the file
            $path = $file->store('uploads/docs');

            // Get file info
            $filename = $file->getClientOriginalName();
            $extension = $file->getClientOriginalExtension();
            $size = $file->getSize();

            // Get the full absolute path for FileDataLoader
            $fullPath = storage_path('app/' . $path);

            return $this->fileVectorize($fullPath, $filename, $size, $extension);

        }
        catch (\Exception $e) {
            Log::error("Error uploading context: {$e->getMessage()}");

            return response()->json(['message' => 'File uploaded unsuccessful']);
        }

    }
}
