/**
 * VectorDrive Manager — Singleton wrapping the native vectordrive VectorDb.
 *
 * Loads the `vectordrive` npm package dynamically (optional dependency),
 * creates a persisted VectorDb in `.sf/vectordrive/`, and exposes status
 * and search/store operations with graceful degradation.
 */

import { mkdirSync } from "node:fs";
import { dirname } from "node:path";

export type VectorBackend = "vectordrive" | "none";

export interface VectordriveStatus {
	backend: VectorBackend;
	version: string | null;
	implementation: string | null;
	initialized: boolean;
	vectorCount: number;
	error: string | null;
	dbPath: string | null;
}

export interface VectorEntry {
	id: string;
	vector: number[];
	metadata?: Record<string, unknown>;
}

export interface SearchResult {
	id: string;
	score: number;
	metadata?: Record<string, unknown>;
}

const DB_DIR = ".sf/vectordrive";
const DB_PATH = `${DB_DIR}/forge.vectors`;
const DIMENSIONS = 384;

function getDbPath(): string {
	const home = process.env.HOME || process.env.USERPROFILE || ".";
	return `${home}/${DB_PATH}`;
}

function ensureDir(path: string): void {
	try {
		mkdirSync(dirname(path), { recursive: true });
	} catch {
		// ignore
	}
}

/** Simple text→vector fallback when no embedding model is available. */
export function textToVector(text: string, dimensions: number = DIMENSIONS): number[] {
	const vec = new Array(dimensions).fill(0);
	const normalized = text.toLowerCase().trim();
	for (let i = 0; i < normalized.length; i++) {
		vec[i % dimensions] += normalized.charCodeAt(i) / 65535;
	}
	const mag = Math.sqrt(vec.reduce((s, v) => s + v * v, 0));
	return mag > 0 ? vec.map((v) => v / mag) : vec;
}

export class VectordriveManager {
	private static instance: VectordriveManager;
	private status: VectordriveStatus | null = null;
	private initPromise: Promise<VectordriveStatus> | null = null;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	private db: any | null = null;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	private vd: any | null = null;

	private constructor() {}

	static getInstance(): VectordriveManager {
		if (!VectordriveManager.instance) {
			VectordriveManager.instance = new VectordriveManager();
		}
		return VectordriveManager.instance;
	}

	async getStatus(): Promise<VectordriveStatus> {
		if (this.status?.initialized) return this.status;
		if (this.initPromise) return this.initPromise;
		this.initPromise = this.probe();
		return this.initPromise;
	}

	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	async getDb(): Promise<any | null> {
		const status = await this.getStatus();
		if (status.backend !== "vectordrive") return null;
		return this.db;
	}

	private async probe(): Promise<VectordriveStatus> {
		const dbPath = getDbPath();

		let vectordrive: any | null = null;
		try {
			const modName = "vectordrive";
			vectordrive = await import(modName);
		} catch (err) {
			this.status = {
				backend: "none",
				version: null,
				implementation: null,
				initialized: true,
				vectorCount: 0,
				error: `vectordrive package not installed: ${err instanceof Error ? err.message : String(err)}`,
				dbPath: null,
			};
			return this.status;
		}

		try {
			this.vd = vectordrive;
			ensureDir(dbPath);

			const VectorDb = vectordrive.VectorDb || vectordrive.VectorDB;
			if (typeof VectorDb !== "function") {
				throw new Error("vectordrive package does not export VectorDb");
			}

			this.db = new VectorDb({
				dimensions: DIMENSIONS,
				storagePath: dbPath,
				distanceMetric: "cosine",
			});

			const count = (await this.db.len()) as number;
			const version = vectordrive.getVersion?.() ?? null;
			const impl = vectordrive.getImplementationType?.() ?? "unknown";

			this.status = {
				backend: "vectordrive",
				version: version?.version ?? null,
				implementation: impl,
				initialized: true,
				vectorCount: count,
				error: null,
				dbPath,
			};
			return this.status;
		} catch (err) {
			this.status = {
				backend: "none",
				version: null,
				implementation: null,
				initialized: true,
				vectorCount: 0,
				error: err instanceof Error ? err.message : String(err),
				dbPath: null,
			};
			return this.status;
		}
	}

	async store(entry: VectorEntry): Promise<boolean> {
		const db = await this.getDb();
		if (!db) return false;
		try {
			await db.insert({
				id: entry.id,
				vector: entry.vector,
				metadata: entry.metadata,
			});
			return true;
		} catch {
			return false;
		}
	}

	async search(vector: number[], k: number): Promise<SearchResult[]> {
		const db = await this.getDb();
		if (!db) return [];
		try {
			const results = await db.search({ vector, k });
			return results.map((r: any) => ({
				id: String(r.id),
				score: Number(r.score),
				metadata: r.metadata,
			}));
		} catch {
			return [];
		}
	}

	async delete(id: string): Promise<boolean> {
		const db = await this.getDb();
		if (!db) return false;
		try {
			return await db.delete(id);
		} catch {
			return false;
		}
	}

	async close(): Promise<void> {
		this.db = null;
		this.vd = null;
		this.status = null;
		this.initPromise = null;
	}
}
