import { z } from "zod";

const numberInput = z.union([z.string(), z.number()]);
const taskTypeSchema = z.enum(["daily_visit", "gift_count", "gift_coin_amount"]);

const giftSchema = z.object({
    coinPrice: numberInput,
    giftId: z.string().trim().min(1, "请选择礼物"),
    iconUrl: z.string().trim().max(512, "礼物图标地址不能超过 512 个字符"),
    name: z.string().trim().max(128, "礼物名称不能超过 128 个字符"),
    priceVersion: z.string().trim().max(64, "价格版本不能超过 64 个字符"),
});

const taskSchema = z.object({
    rewardResourceGroupId: numberInput,
    targetValue: numberInput,
    taskKey: z
        .string()
        .trim()
        .regex(/^[a-z][a-z0-9_-]{1,63}$/, "任务键需以小写字母开头，仅可使用小写字母、数字、_、-"),
    taskType: taskTypeSchema,
});

const rankRewardSchema = z.object({
    rankFrom: numberInput,
    rankTo: numberInput,
    resourceGroupId: numberInput,
});

const assetSchema = z.object({
    assetKey: z
        .string()
        .trim()
        .regex(/^[a-z][a-z0-9_.-]{1,63}$/, "素材键格式错误"),
    label: z.string(),
    locale: z.string(),
    mediaType: z.enum(["image", "webp", "svga", "pag"]),
    url: z.string().trim().max(1024, "素材地址不能超过 1024 个字符"),
});

export const activityTemplateFormSchema = z
    .object({
        activityType: z.literal("gift_challenge"),
        allRegions: z.boolean(),
        assets: z.array(assetSchema).max(100, "素材最多配置 100 项"),
        dailyLeaderboardSize: numberInput,
        dailyRankRewards: z.array(rankRewardSchema),
        displayConfigJson: z.string().max(64 * 1024, "展示配置不能超过 64KB"),
        gifts: z.array(giftSchema).max(20, "礼物最多配置 20 个"),
        name: z.string().trim().min(1, "请输入内部名称").max(128, "内部名称不能超过 128 个字符"),
        regionIds: z.array(z.union([z.string(), z.number()])).max(100, "区域最多选择 100 个"),
        revision: numberInput.optional(),
        rulesAr: z.string().max(32 * 1024, "阿语规则不能超过 32KB"),
        rulesEn: z.string().max(32 * 1024, "英语规则不能超过 32KB"),
        tasks: z.array(taskSchema).max(30, "每日任务最多配置 30 项"),
        templateCode: z
            .string()
            .trim()
            .regex(/^[a-z][a-z0-9_-]{2,63}$/, "模版编码需以小写字母开头，长度 3-64，仅可使用小写字母、数字、_、-"),
        templateId: z.string().optional(),
        timeRange: z.object({
            endMs: z.union([z.string(), z.number()]),
            startMs: z.union([z.string(), z.number()]),
        }),
        titleAr: z.string().trim().max(160, "阿语标题不能超过 160 个字符"),
        titleEn: z.string().trim().max(160, "英语标题不能超过 160 个字符"),
        totalLeaderboardSize: numberInput,
        totalRankRewards: z.array(rankRewardSchema),
    })
    .superRefine((value, context) => {
        const startMs = numberValue(value.timeRange.startMs);
        const endMs = numberValue(value.timeRange.endMs);
        if ((startMs > 0 || endMs > 0) && (startMs <= 0 || endMs <= startMs)) {
            context.addIssue({ code: "custom", message: "活动结束时间必须晚于开始时间", path: ["timeRange"] });
        }
        if (startMs > 0 && endMs - startMs > 366 * 24 * 60 * 60 * 1000) {
            context.addIssue({ code: "custom", message: "单次活动周期不能超过 366 天", path: ["timeRange"] });
        }
        if (value.allRegions && value.regionIds.length) {
            context.addIssue({ code: "custom", message: "全部区域与指定区域不能同时配置", path: ["regionIds"] });
        }
        requireIntegerRange(
            context,
            ["dailyLeaderboardSize"],
            value.dailyLeaderboardSize,
            3,
            1000,
            "日榜上榜人数必须是 3-1000 的整数",
        );
        requireIntegerRange(
            context,
            ["totalLeaderboardSize"],
            value.totalLeaderboardSize,
            3,
            1000,
            "总榜上榜人数必须是 3-1000 的整数",
        );
        validateUnique(
            context,
            value.gifts.map((gift) => gift.giftId),
            ["gifts"],
            "同一礼物不能重复配置",
        );
        validateUnique(
            context,
            value.tasks.map((task) => task.taskKey),
            ["tasks"],
            "任务键不能重复",
        );
        if (value.tasks.filter((task) => task.taskType === "daily_visit").length > 1) {
            context.addIssue({
                code: "custom",
                message: "每日进入活动任务最多配置 1 个",
                path: ["tasks"],
            });
        }
        value.tasks.forEach((task, index) => {
            requirePositiveInteger(context, ["tasks", index, "targetValue"], task.targetValue, "任务目标必须是正整数");
            if (task.taskType === "daily_visit" && numberValue(task.targetValue) !== 1) {
                context.addIssue({
                    code: "custom",
                    message: "每日进入活动任务的目标固定为 1",
                    path: ["tasks", index, "targetValue"],
                });
            }
            validateOptionalPositiveInteger(
                context,
                ["tasks", index, "rewardResourceGroupId"],
                task.rewardResourceGroupId,
                "请选择有效奖励资源组",
            );
        });
        validateRankRewards(
            context,
            value.dailyRankRewards,
            numberValue(value.dailyLeaderboardSize),
            "dailyRankRewards",
        );
        validateRankRewards(
            context,
            value.totalRankRewards,
            numberValue(value.totalLeaderboardSize),
            "totalRankRewards",
        );
        try {
            const parsed = JSON.parse(value.displayConfigJson || "{}");
            if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
                throw new Error("展示配置必须是 JSON 对象");
            }
        } catch (error) {
            context.addIssue({
                code: "custom",
                message: error instanceof Error ? error.message : "展示配置 JSON 格式错误",
                path: ["displayConfigJson"],
            });
        }
    });

export type ActivityTemplateForm = z.infer<typeof activityTemplateFormSchema>;

export function validateActivityTemplateForPublish(form: ActivityTemplateForm) {
    const issues: Array<{ message: string; path: Array<number | string> }> = [];
    const push = (path: Array<number | string>, message: string) => issues.push({ message, path });
    const startMs = numberValue(form.timeRange.startMs);
    const endMs = numberValue(form.timeRange.endMs);
    if (startMs <= 0 || endMs <= startMs) {
        push(["timeRange"], "发布前必须配置有效活动时间");
    }
    if (!form.allRegions && !form.regionIds.length) {
        push(["regionIds"], "发布前必须选择投放区域");
    }
    if (!form.titleEn.trim()) {
        push(["titleEn"], "发布前必须填写英语标题");
    }
    if (!form.rulesEn.trim()) {
        push(["rulesEn"], "发布前必须填写英语规则");
    }
    if (!form.gifts.length) {
        push(["gifts"], "发布前至少配置 1 个礼物");
    }
    form.gifts.forEach((gift, index) => {
        if (!gift.name || !gift.iconUrl || numberValue(gift.coinPrice) <= 0 || !gift.priceVersion) {
            push(["gifts", index], "礼物缺少名称、图标、金币价格或价格版本快照");
        }
    });
    if (!form.tasks.length) {
        push(["tasks"], "发布前至少配置 1 个每日任务");
    }
    form.tasks.forEach((task, index) => {
        if (numberValue(task.rewardResourceGroupId) <= 0) {
            push(["tasks", index, "rewardResourceGroupId"], "发布前必须选择任务奖励资源组");
        }
    });
    for (const [key, rewards] of [
        ["dailyRankRewards", form.dailyRankRewards],
        ["totalRankRewards", form.totalRankRewards],
    ] as const) {
        if (!rewards.length) {
            push([key], key === "dailyRankRewards" ? "发布前必须配置日榜奖励" : "发布前必须配置总榜奖励");
        }
        rewards.forEach((reward, index) => {
            if (numberValue(reward.resourceGroupId) <= 0) {
                push([key, index, "resourceGroupId"], "发布前必须选择榜单奖励资源组");
            }
        });
    }
    return issues;
}

function validateRankRewards(
    context: z.RefinementCtx,
    rewards: Array<{ rankFrom: string | number; rankTo: string | number; resourceGroupId: string | number }>,
    leaderboardSize: number,
    pathKey: string,
) {
    const sorted = rewards
        .map((reward, index) => ({
            ...reward,
            index,
            from: numberValue(reward.rankFrom),
            to: numberValue(reward.rankTo),
        }))
        .sort((left, right) => left.from - right.from);
    let previousTo = 0;
    sorted.forEach((reward) => {
        if (
            !Number.isInteger(reward.from) ||
            !Number.isInteger(reward.to) ||
            reward.from <= 0 ||
            reward.to < reward.from
        ) {
            context.addIssue({
                code: "custom",
                message: "奖励名次必须是有效闭区间",
                path: [pathKey, reward.index, "rankFrom"],
            });
        }
        if (leaderboardSize > 0 && reward.to > leaderboardSize) {
            context.addIssue({
                code: "custom",
                message: "奖励名次不能超过上榜人数",
                path: [pathKey, reward.index, "rankTo"],
            });
        }
        if (previousTo > 0 && reward.from <= previousTo) {
            context.addIssue({
                code: "custom",
                message: "奖励名次区间不能重叠",
                path: [pathKey, reward.index, "rankFrom"],
            });
        }
        previousTo = Math.max(previousTo, reward.to);
        validateOptionalPositiveInteger(
            context,
            [pathKey, reward.index, "resourceGroupId"],
            reward.resourceGroupId,
            "请选择有效奖励资源组",
        );
    });
}

function validateUnique(context: z.RefinementCtx, values: string[], path: string[], message: string) {
    const normalized = values.map((value) => value.trim()).filter(Boolean);
    if (new Set(normalized).size !== normalized.length) {
        context.addIssue({ code: "custom", message, path });
    }
}

function requirePositiveInteger(
    context: z.RefinementCtx,
    path: Array<number | string>,
    value: unknown,
    message: string,
) {
    const number = numberValue(value);
    if (!Number.isInteger(number) || number <= 0) {
        context.addIssue({ code: "custom", message, path });
    }
}

function validateOptionalPositiveInteger(
    context: z.RefinementCtx,
    path: Array<number | string>,
    value: unknown,
    message: string,
) {
    if (String(value ?? "").trim() === "") {
        return;
    }
    requirePositiveInteger(context, path, value, message);
}

function requireIntegerRange(
    context: z.RefinementCtx,
    path: string[],
    value: unknown,
    min: number,
    max: number,
    message: string,
) {
    const number = numberValue(value);
    if (!Number.isInteger(number) || number < min || number > max) {
        context.addIssue({ code: "custom", message, path });
    }
}

function numberValue(value: unknown) {
    const number = Number(value);
    return Number.isFinite(number) ? number : 0;
}
