import { z } from "zod";

const identifier = z.string().min(1).max(64);

/**
 * Cahier des charges technique, section 2.2 : une cible n'est pas un selecteur mais
 * une liste ordonnee de candidats, du plus stable au plus fragile. L'ordre du tableau
 * fait foi ; le SDK l'applique tel quel.
 */
export const targetCandidateSchema = z.discriminatedUnion("kind", [
	z.object({ kind: z.literal("testid"), value: z.string().min(1) }),
	z.object({ kind: z.literal("id"), value: z.string().min(1) }),
	z.object({
		kind: z.literal("aria"),
		role: z.string().min(1),
		name: z.string().min(1),
	}),
	z.object({
		kind: z.literal("text"),
		tag: z.string().min(1),
		value: z.string().min(1),
	}),
	z.object({ kind: z.literal("css"), value: z.string().min(1) }),
]);

export const targetSchema = z.object({
	candidates: z.array(targetCandidateSchema).min(1),
});

const placementSchema = z.enum(["top", "bottom", "left", "right"]);
const advanceSchema = z.enum(["button", "element-click", "element-input"]);

const tooltipStepSchema = z.object({
	id: identifier,
	next: identifier.optional(),
	type: z.literal("tooltip"),
	title: z.string().min(1),
	body: z.string().default(""),
	target: targetSchema,
	placement: placementSchema.default("bottom"),
	highlight: z.boolean().default(false),
	advance: advanceSchema.default("button"),
});

const modalStepSchema = z.object({
	id: identifier,
	next: identifier.optional(),
	type: z.literal("modal"),
	title: z.string().min(1),
	body: z.string().default(""),
});

export const questionOptionSchema = z.object({
	id: identifier,
	label: z.string().min(1),
	next: identifier,
});

const questionStepSchema = z.object({
	id: identifier,
	next: identifier.optional(),
	type: z.literal("question"),
	title: z.string().min(1),
	options: z.array(questionOptionSchema).min(2),
});

export const stepSchema = z.discriminatedUnion("type", [
	tooltipStepSchema,
	modalStepSchema,
	questionStepSchema,
]);

export const triggerSchema = z.object({
	urlPattern: z.string().min(1),
	frequency: z.enum(["once", "always"]).default("once"),
});

export const audienceSchema = z.object({
	locales: z.array(z.string().min(2)).optional(),
	match: z.record(z.string(), z.array(z.string())).optional(),
});

const tourShapeSchema = z.object({
	id: identifier,
	version: z.number().int().positive(),
	name: z.string().min(1),
	trigger: triggerSchema,
	audience: audienceSchema.optional(),
	steps: z.array(stepSchema).min(1),
});

export const tourDraftSchema = z.object({
	name: z.string().min(1),
	trigger: triggerSchema,
	audience: audienceSchema.optional(),
	steps: z.array(stepSchema).min(1),
});

type TourShape = z.infer<typeof tourShapeSchema>;

function refineTourGraph(
	tour: { steps: TourShape["steps"] },
	ctx: z.RefinementCtx,
): void {
	const stepIds = new Set<string>();

	tour.steps.forEach((step, index) => {
		if (stepIds.has(step.id)) {
			ctx.addIssue({
				code: "custom",
				path: ["steps", index, "id"],
				message: `L'identifiant d'etape ${step.id} est utilise plusieurs fois.`,
			});
		}
		stepIds.add(step.id);
	});

	tour.steps.forEach((step, index) => {
		if (step.next !== undefined && !stepIds.has(step.next)) {
			ctx.addIssue({
				code: "custom",
				path: ["steps", index, "next"],
				message: `L'etape ${step.id} pointe vers une etape inexistante.`,
			});
		}

		if (step.type !== "question") return;

		const optionIds = new Set<string>();
		step.options.forEach((option, optionIndex) => {
			if (optionIds.has(option.id)) {
				ctx.addIssue({
					code: "custom",
					path: ["steps", index, "options", optionIndex, "id"],
					message: `L'option ${option.id} est definie plusieurs fois.`,
				});
			}
			optionIds.add(option.id);

			if (!stepIds.has(option.next)) {
				ctx.addIssue({
					code: "custom",
					path: ["steps", index, "options", optionIndex, "next"],
					message: `L'option ${option.id} pointe vers une etape inexistante.`,
				});
			}
		});
	});
}

/**
 * Les champs inconnus sont volontairement ignores plutot que rejetes : un SDK ancien
 * doit pouvoir lire un parcours recent (section 7.1, compatibilite ascendante).
 */
export const tourSchema = tourShapeSchema.superRefine(refineTourGraph);

export const tourDraftGraphSchema =
	tourDraftSchema.superRefine(refineTourGraph);

export type TargetCandidate = z.infer<typeof targetCandidateSchema>;
export type Target = z.infer<typeof targetSchema>;
export type QuestionOption = z.infer<typeof questionOptionSchema>;
export type Step = z.infer<typeof stepSchema>;
export type Trigger = z.infer<typeof triggerSchema>;
export type Audience = z.infer<typeof audienceSchema>;
export type Tour = TourShape;
export type TourDraft = z.infer<typeof tourDraftSchema>;
