// qs/Services/Files.qml
pragma Singleton

import QtQuick
import Quickshell
import Quickshell.Io
import ZShell.Config

Singleton {
	id: root

	property list<var> results: []

	property string _pendingQuery: ""
	property int _generation: 0

	readonly property var _imageExts: ({
			"png": true,
			"jpg": true,
			"jpeg": true,
			"gif": true,
			"bmp": true,
			"webp": true,
			"svg": true,
			"tiff": true,
			"tif": true,
			"ico": true,
			"heic": true,
			"avif": true
		})

	// freedesktop mimetype-icon names; extend as needed
	readonly property var _extIcons: ({
			// images
			"png": "image-x-generic",
			"jpg": "image-x-generic",
			"jpeg": "image-x-generic",
			"gif": "image-x-generic",
			"bmp": "image-x-generic",
			"webp": "image-x-generic",
			"svg": "image-x-generic",
			"tiff": "image-x-generic",
			"tif": "image-x-generic",
			"ico": "image-x-generic",
			"heic": "image-x-generic",
			"avif": "image-x-generic",
			// documents
			"pdf": "application-pdf",
			"doc": "x-office-document",
			"docx": "x-office-document",
			"odt": "x-office-document",
			"txt": "text-x-generic",
			"md": "text-x-generic",
			"xls": "x-office-spreadsheet",
			"xlsx": "x-office-spreadsheet",
			"ods": "x-office-spreadsheet",
			"ppt": "x-office-presentation",
			"pptx": "x-office-presentation",
			"odp": "x-office-presentation",
			// archives
			"zip": "package-x-generic",
			"tar": "package-x-generic",
			"gz": "package-x-generic",
			"xz": "package-x-generic",
			"7z": "package-x-generic",
			"rar": "package-x-generic",
			// audio/video
			"mp3": "audio-x-generic",
			"flac": "audio-x-generic",
			"wav": "audio-x-generic",
			"ogg": "audio-x-generic",
			"mp4": "video-x-generic",
			"mkv": "video-x-generic",
			"webm": "video-x-generic",
			"avi": "video-x-generic",
			"mov": "video-x-generic",
			// code
			"js": "text-x-script",
			"ts": "text-x-script",
			"py": "text-x-script",
			"sh": "text-x-script",
			"qml": "text-x-script",
			"cpp": "text-x-c++src",
			"c": "text-x-csrc",
			"h": "text-x-chdr",
			"json": "application-json",
			"html": "text-html",
			"css": "text-css"
		})

	function showInFolder(path: string): void {
		const explorer = Config.general.apps.explorer;
		let args = [];

		if (explorer.includes("dolphin"))
			args = [...explorer, "--select", path];
		else
			args = [...explorer, path];

		if (Config.launcher.uwsm)
			Quickshell.execDetached(["app2unit", "--", ...args]);
		else
			Quickshell.execDetached([...args]);
	}

	function launch(command: list<string>): void {
		if (Config.launcher.uwsm)
			Quickshell.execDetached(["app2unit", "--"].concat(command));
		else
			Quickshell.execDetached(command);
	}

	function transformSearch(search: string): string {
		return search.slice(Config.launcher.actionPrefix.length + "files ".length);
	}

	function query(search: string): list<var> {
		search = transformSearch(search);
		if (search !== _pendingQuery) {
			_pendingQuery = search;
			debounceTimer.restart();
		}
		return results;
	}

	function _extOf(name: string): string {
		const i = name.lastIndexOf(".");
		// no dot, or dotfile with no real extension (".bashrc" -> treat as no ext)
		if (i <= 0)
			return "";
		return name.slice(i + 1).toLowerCase();
	}

	function _iconFor(name: string, isDir: bool): string {
		if (isDir)
			return "folder";
		const ext = _extOf(name);
		return _extIcons[ext] ?? "text-x-generic";
	}

	function _buildEntry(path: string): var {
		const name = path.split("/").pop();
		const ext = _extOf(name);
		// trailing slash from plocate's directory output (if -d/dir results ever appear)
		const isDir = path.endsWith("/");

		return {
			path: path,
			name: name,
			icon: _iconFor(name, isDir),
			isImage: !isDir && (_imageExts[ext] ?? false)
		};
	}

	Timer {
		id: debounceTimer
		interval: 120
		onTriggered: root._runQuery(root._pendingQuery)
	}

	function _runQuery(search: string): void {
		_generation++;

		if (!search) {
			proc.running = false;
			results = [];
			return;
		}

		if (proc.running)
			proc.running = false;

		proc.buffer = [];
		proc.myGeneration = _generation;
		proc.command = ["plocate", "-i", "-b", "--limit", "50", search];
		proc.running = true;
	}

	Process {
		id: proc

		property var buffer: []
		property int myGeneration: 0

		stdout: SplitParser {
			onRead: line => proc.buffer.push(root._buildEntry(line))
		}

		onRunningChanged: {
			if (!running && myGeneration === root._generation)
				root.results = buffer;
		}
	}
}
