pragma Singleton
pragma ComponentBehavior: Bound

import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common

Singleton {
    id: root

    property bool dsearchAvailable: false
    property int searchIdCounter: 0
    property int indexVersion: 0
    property bool supportsTypeFilter: false
    property bool versionChecked: false

    signal searchResultsReceived(var results)
    signal statsReceived(var stats)
    signal errorOccurred(string error)

    Process {
        id: checkProcess
        command: ["sh", "-c", "command -v dsearch"]
        running: true

        stdout: SplitParser {
            onRead: line => {
                if (line && line.trim().length > 0) {
                    root.dsearchAvailable = true;
                }
            }
        }

        onExited: exitCode => {
            if (exitCode !== 0) {
                root.dsearchAvailable = false;
            } else {
                root._checkVersion();
            }
        }
    }

    function _checkVersion() {
        Proc.runCommand("dsearch-version", ["dsearch", "version", "--json"], (stdout, exitCode) => {
            root.versionChecked = true;
            if (exitCode !== 0)
                return;
            const response = JSON.parse(stdout);
            root.indexVersion = response.index_schema || 0;
            root.supportsTypeFilter = root.indexVersion >= 2;
        });
    }

    function ping(callback) {
        if (!dsearchAvailable) {
            if (callback) {
                callback({
                    "error": "dsearch not available"
                });
            }
            return;
        }

        Proc.runCommand("dsearch-ping", ["dsearch", "ping", "--json"], (stdout, exitCode) => {
            if (callback) {
                if (exitCode === 0) {
                    try {
                        const response = JSON.parse(stdout);
                        callback({
                            "result": response
                        });
                    } catch (e) {
                        callback({
                            "error": "failed to parse ping response"
                        });
                    }
                } else {
                    callback({
                        "error": "ping failed"
                    });
                }
            }
        });
    }

    function search(query, params, callback) {
        if (!query || query.length === 0) {
            if (callback) {
                callback({
                    "error": "query is required"
                });
            }
            return;
        }

        if (!dsearchAvailable) {
            if (callback) {
                callback({
                    "error": "dsearch not available"
                });
            }
            return;
        }

        const args = ["dsearch", "search", query, "--json"];

        if (params) {
            if (params.limit !== undefined) {
                args.push("-n", String(params.limit));
            }
            if (params.type) {
                args.push("-t", params.type);
            }
            if (params.ext) {
                args.push("-e", params.ext);
            }
            if (params.folder) {
                args.push("--folder", params.folder);
            }
            if (params.field) {
                args.push("-f", params.field);
            }
            if (params.fuzzy) {
                args.push("--fuzzy");
            }
            if (params.sort) {
                args.push("--sort", params.sort);
            }
            if (params.desc !== undefined) {
                args.push("--desc=" + (params.desc ? "true" : "false"));
            }
            if (params.minSize !== undefined) {
                args.push("--min-size", String(params.minSize));
            }
            if (params.maxSize !== undefined) {
                args.push("--max-size", String(params.maxSize));
            }
        }

        const procId = "dsearch-search-" + (params?.type || "all");
        Proc.runCommand(procId, args, (stdout, exitCode) => {
            if (exitCode === 0) {
                try {
                    const response = JSON.parse(stdout);
                    searchResultsReceived(response);
                    if (callback) {
                        callback({
                            "result": response
                        });
                    }
                } catch (e) {
                    const error = "failed to parse search response";
                    errorOccurred(error);
                    if (callback) {
                        callback({
                            "error": error
                        });
                    }
                }
            } else if (exitCode === 124) {
                const error = "search timed out";
                errorOccurred(error);
                if (callback) {
                    callback({
                        "error": error
                    });
                }
            } else {
                const error = "search failed";
                errorOccurred(error);
                if (callback) {
                    callback({
                        "error": error
                    });
                }
            }
        }, 100, 5000);
    }

    function rediscover() {
        checkProcess.running = true;
    }
}
