import { and, eq } from "drizzle-orm";
import { db } from "../../../db/db";
import { repositoriesTable } from "../../../db/schema";
import { logger } from "@zerobyte/core/node";
import { toMessage } from "~/server/utils/errors";
import { REPOSITORY_BASE } from "~/server/core/constants";
import { repositoryConfigSchema } from "@zerobyte/core/restic";

type MigrationError = { name: string; error: string };

const asString = (value: unknown): string | null => {
	if (typeof value !== "string") {
		return null;
	}

	return value;
};

const hasValue = (value: string | null): value is string => {
	return value !== null && value.trim() !== "";
};

const trimTrailingSlashes = (value: string): string => {
	return value.replace(/\/+$/, "");
};

const isPathAlreadyMigrated = (path: string, name: string): boolean => {
	const normalizedPath = trimTrailingSlashes(path);
	return normalizedPath.endsWith(`/${name}`);
};

const buildPath = (path: string | null, name: string): string => {
	if (path === null || path.trim() === "") {
		return `${REPOSITORY_BASE}/${name}`;
	}

	return `${trimTrailingSlashes(path)}/${name}`;
};

const execute = async () => {
	const errors: MigrationError[] = [];
	const localRepositories = await db.query.repositoriesTable.findMany({ where: { type: "local" } });
	let migratedCount = 0;

	for (const repository of localRepositories) {
		try {
			const config = repository.config as Record<string, unknown>;

			if (typeof config !== "object" || config === null || Array.isArray(config)) {
				errors.push({
					name: `repository:${repository.id}`,
					error: "Repository config is not a valid JSON object",
				});
				continue;
			}

			const localRepositoryName = asString(config.name);

			if (!hasValue(localRepositoryName) || config.isExistingRepository === true) {
				continue;
			}

			const currentPath = asString(config.path);
			if (hasValue(currentPath) && isPathAlreadyMigrated(currentPath, localRepositoryName)) {
				continue;
			}

			config.path = buildPath(currentPath, localRepositoryName);

			const newConfigResult = repositoryConfigSchema.safeParse(config);
			if (!newConfigResult.success) {
				errors.push({
					name: `repository:${repository.id}`,
					error: `Validation failed for updated repository config: ${newConfigResult.error.message}`,
				});
				continue;
			}
			const newConfig = newConfigResult.data;

			await db
				.update(repositoriesTable)
				.set({ config: newConfig, updatedAt: Date.now() })
				.where(and(eq(repositoriesTable.id, repository.id), eq(repositoriesTable.type, "local")));

			migratedCount += 1;
		} catch (err) {
			errors.push({
				name: `repository:${repository.id}`,
				error: toMessage(err),
			});
		}
	}

	logger.info(`Migration 00004-concat-path-name updated ${migratedCount} local repositories.`);

	return { success: errors.length === 0, errors };
};

export const v00004 = {
	execute,
	id: "00004-concat-path-name",
	type: "maintenance" as const,
};
