import { randomUUID } from 'crypto';
import { prisma } from '@/lib/core/prisma';
import type { ResolvedConfig, ConfigType } from '@/types';
export { substituteAll, substituteFileReferences, substituteVariables } from '@/lib/test-config/substitution';

interface ResolvedConfigs {
    variables: Record<string, string>;
    files: Record<string, string>;
    allConfigs: ResolvedConfig[];
}

const RESOLVABLE_CONFIG_TYPES: ConfigType[] = ['URL', 'APP_ID', 'VARIABLE', 'RANDOM_STRING', 'FILE'];

function generateRandomStringValue(generationType: string, attempt = 0): string {
    switch (generationType) {
        case 'TIMESTAMP_UNIX':
            return Date.now().toString();
        case 'TIMESTAMP_DATETIME': {
            const now = new Date(Date.now() + attempt * 1000);
            const pad = (n: number, len = 2) => String(n).padStart(len, '0');
            return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
        }
        case 'UUID':
            return randomUUID().replace(/-/g, '');
        default:
            return randomUUID().replace(/-/g, '');
    }
}

export async function resolveConfigs(projectId: string, testCaseId?: string): Promise<ResolvedConfigs> {
    const projectConfigs = await prisma.projectConfig.findMany({
        where: {
            projectId,
            type: {
                in: RESOLVABLE_CONFIG_TYPES
            }
        },
        orderBy: { createdAt: 'asc' }
    });

    const testCaseConfigs = testCaseId
        ? await prisma.testCaseConfig.findMany({
            where: {
                testCaseId,
                type: {
                    in: RESOLVABLE_CONFIG_TYPES
                }
            },
            orderBy: { createdAt: 'asc' }
        })
        : [];

    const merged = new Map<string, ResolvedConfig>();

    for (const pc of projectConfigs) {
        merged.set(pc.name, {
            name: pc.name,
            type: pc.type as ConfigType,
            value: pc.value,
            masked: pc.masked,
            filename: pc.filename ?? undefined,
            source: 'project',
        });
    }

    for (const tc of testCaseConfigs) {
        merged.set(tc.name, {
            name: tc.name,
            type: tc.type as ConfigType,
            value: tc.value,
            masked: tc.masked,
            filename: tc.filename ?? undefined,
            source: 'test-case',
        });
    }

    const generatedValues = new Set<string>();
    for (const config of merged.values()) {
        if (config.type === 'RANDOM_STRING') {
            let attempt = 0;
            let value = generateRandomStringValue(config.value, attempt);
            while (generatedValues.has(value)) {
                attempt += 1;
                value = generateRandomStringValue(config.value, attempt);
            }
            generatedValues.add(value);
            config.value = value;
        }
    }

    const variables: Record<string, string> = {};
    const files: Record<string, string> = {};

    for (const config of merged.values()) {
        if (config.type === 'FILE') {
            files[config.name] = config.value;
            if (config.filename) {
                files[config.filename] = config.value;
            }
        } else {
            variables[config.name] = config.value;
        }
    }

    return {
        variables,
        files,
        allConfigs: Array.from(merged.values()),
    };
}
