// Copyright (c) Microsoft Corporation and Henry Lucco.
// Licensed under the MIT License.

import express, { Request, Response } from "express";
import {
    AgentCache,
    AgentCacheFactory,
    MatchResult,
    RequestAction,
} from "agent-cache";
import { existsSync } from "fs";
import path from "path";
import { NodeType, SchemaParser } from "@typeagent/action-schema";

type SchemaEntry = {
    name: string;
    camelCaseName: string;
    value: string;
    id?: string;
};

// Create an instance of an Express application
const app = express();

// Define the port number
const port = `10482`;

// The cache file to serve
const cacheFile = "data/v5_sample.json";

// the cache
let agentCache: AgentCache;

// load the schema
let schemas: SchemaEntry[] = [];
const schemaFile: string = `../../packages/agents/settings/src/settingsActionSchema.ts`;

// Set up a route for the root URL ('/')
app.get("/", async (req: Request, res: Response) => {
    const cachedItem: string = Array.isArray(req.query["s"])
        ? (req.query["s"].join(" ") as string)
        : (req.query["s"] as string);

    if (cachedItem) {
        console.log(`Received request with s=${cachedItem}`);

        const matches: MatchResult[] = await agentCache.constructionStore.match(
            cachedItem,
            {},
        );
        if (matches.length > 0) {
            const hits: RequestAction[] = [];

            matches.forEach((construction) => {
                // add the id of the schema (if it exists) to the action
                construction.match.actions.forEach((a) => {
                    const schema = schemas.find(
                        (s) => s.camelCaseName === a.action.actionName,
                    );
                    if (schema) {
                        (a.action as any).id = schema.id;
                    }
                });

                hits.push(construction.match);
            });

            res.send(`${JSON.stringify(hits, null, 0)}`);
        } else {
            res.status(404).send('{ "error": "No matches found." }');
        }
    } else {
        res.status(400).send(
            '{ "error": "Please provide a query parameter \'s\' containing the cache entry to check." }',
        );
    }
});

app.get(`/test`, async (req: Request, res: Response) => {
    res.sendFile(path.resolve("src/test.html"));
});

// Start the server and listen on the specified port
app.listen(port, async () => {
    console.log(`Server is running at http://localhost:${port}`);
    console.log(`Serving cache file from: ${cacheFile}`);

    const cacheFactory = new AgentCacheFactory();
    agentCache = cacheFactory.create();

    if (existsSync(cacheFile)) {
        await agentCache.constructionStore.load(cacheFile);
    } else {
        throw new Error(`Cache file does not exist: ${cacheFile}`);
        process.exit(1);
    }

    if (existsSync(schemaFile)) {
        schemas = getActionSchema(schemaFile);
    }
});

function getActionSchema(filePath: string): SchemaEntry[] {
    const schema = new SchemaParser();
    schema.loadSchema(filePath);
    const types = schema.actionTypeNames();
    const result: SchemaEntry[] = [];
    for (const type of types) {
        const node = schema.openActionNode(type);
        let schemaText = node?.symbol.value!;
        for (const subType of node!.children) {
            if (subType.symbol.type === NodeType.TypeReference) {
                schemaText += "\n\n" + subType.symbol.value;
            }
        }
        const idChild = node!.children.find((c) => c.symbol.name === "id");
        const rawId = idChild?.symbol.value ?? "";
        const idValue = rawId ? JSON.parse(rawId.trim()) : undefined;
        const interfaceName = node?.symbol.name!;
        const camelCaseName =
            interfaceName.charAt(0).toLowerCase() + interfaceName.slice(1);
        result.push({
            name: interfaceName,
            camelCaseName,
            value: schemaText,
            id: idValue,
        });
    }
    return result;
}
