import { z } from "zod";

const numberInput = z.union([z.string(), z.number()]);
const optionalNumberInput = numberInput.optional();
const statusSchema = z.enum(["draft", "active", "paused", "archived"]);
const collectionTypeSchema = z.enum(["ordinary", "activity_limited"]);
const achievementTypeSchema = z.enum(["ordinary", "activity_limited"]);
const autoPinPolicySchema = z.enum(["none", "if_empty", "always"]);

const conditionSchema = z.object({
    dimensionFilterJson: z.string().optional(),
    metricType: z.string().trim().min(1, "请输入指标类型").max(80, "指标类型不能超过 80 个字符"),
    targetUnit: z.string().trim().min(1, "请选择目标单位").max(32, "目标单位不能超过 32 个字符"),
    targetValue: numberInput,
});

export const achievementFormSchema = z
    .object({
        achievementType: achievementTypeSchema,
        autoPinPolicy: autoPinPolicySchema,
        collectionId: z.string().trim().max(80, "合集 ID 不能超过 80 个字符").optional(),
        collectionType: collectionTypeSchema,
        conditions: z.array(conditionSchema).min(1, "至少配置一个达成条件").max(10, "最多配置 10 个达成条件"),
        description: z.string().trim().max(240, "描述不能超过 240 个字符").optional(),
        displayConfigJson: z.string().optional(),
        effectiveFrom: z.string().optional(),
        effectiveTo: z.string().optional(),
        honorVisibility: z.boolean(),
        primaryBadgeResourceId: optionalNumberInput,
        rewardResourceGroupId: optionalNumberInput,
        sortOrder: optionalNumberInput,
        status: statusSchema,
        title: z.string().trim().min(1, "请输入成就名称").max(64, "成就名称不能超过 64 个字符"),
    })
    .superRefine((value, context) => {
        validateOptionalId(context, "primaryBadgeResourceId", value.primaryBadgeResourceId, "展示徽章必须是有效资源");
        validateOptionalId(context, "rewardResourceGroupId", value.rewardResourceGroupId, "奖励资源组必须是有效资源组");
        validateInteger(context, "sortOrder", value.sortOrder || 0, "排序必须是整数");
        validateJsonObject(context, "displayConfigJson", value.displayConfigJson, "展示配置 JSON 不正确");
        const fromMs = datetimeToMs(value.effectiveFrom);
        const toMs = datetimeToMs(value.effectiveTo);
        if (fromMs > 0 && toMs > 0 && toMs <= fromMs) {
            context.addIssue({ code: "custom", message: "结束时间必须晚于开始时间", path: ["effectiveTo"] });
        }
        value.conditions.forEach((condition, index) => {
            validatePositiveInteger(
                context,
                `conditions.${index}.targetValue`,
                condition.targetValue,
                "目标值必须是正整数",
            );
            validateJsonObject(
                context,
                `conditions.${index}.dimensionFilterJson`,
                condition.dimensionFilterJson,
                "维度过滤 JSON 不正确",
            );
        });
    });

export type AchievementForm = z.infer<typeof achievementFormSchema>;

function validatePositiveInteger(context: z.RefinementCtx, path: string, value: unknown, message: string) {
    const numeric = Number(value);
    if (!Number.isInteger(numeric) || numeric <= 0) {
        context.addIssue({ code: "custom", message, path: path.split(".") });
    }
}

function validateOptionalId(context: z.RefinementCtx, path: string, value: unknown, message: string) {
    const raw = String(value ?? "").trim();
    if (!raw) {
        return;
    }
    validatePositiveInteger(context, path, raw, message);
}

function validateInteger(context: z.RefinementCtx, path: string, value: unknown, message: string) {
    const numeric = Number(value);
    if (!Number.isInteger(numeric)) {
        context.addIssue({ code: "custom", message, path: [path] });
    }
}

function validateJsonObject(context: z.RefinementCtx, path: string, value: unknown, message: string) {
    const raw = String(value || "").trim();
    if (!raw) {
        return;
    }
    try {
        const parsed = JSON.parse(raw);
        if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
            context.addIssue({ code: "custom", message, path: path.split(".") });
        }
    } catch {
        context.addIssue({ code: "custom", message, path: path.split(".") });
    }
}

function datetimeToMs(value: unknown) {
    const raw = String(value || "").trim();
    if (!raw) {
        return 0;
    }
    const parsed = new Date(raw).getTime();
    return Number.isFinite(parsed) ? parsed : 0;
}
