import { parseTja } from './parse';
import { factorize } from './factorize';
import { join } from 'path';
import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs';
import { execSync } from 'child_process';

/**
 * 예측 엔진: 모델 실행을 위해 Python 환경을 호출
 */
function runInference(workingDir: string, factors: Record<string, number>): number {
    const tempFactorPath = join(workingDir, `temp_feat_${Date.now()}.json`);
    writeFileSync(tempFactorPath, JSON.stringify(factors));

    const pythonScript = `
import json, torch, sys
import numpy as np
import torch.nn as nn

# 모델 정의 (학습된 구조와 동일해야 함)
class DifficultyNet(nn.Module):
    def __init__(self, input_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, 128),
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.Dropout(0.4),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, 1),
            nn.Sigmoid()
        )
    def forward(self, x): return self.net(x)

def predict():
    try:
        with open('${tempFactorPath}', 'r') as f: factors = json.load(f)
        with open('${join(workingDir, 'scaler.json')}', 'r') as f: s = json.load(f)

        X = np.array([[factors[col] for col in s['cols']]], dtype=np.float32)
        
        # 정규화 (Robust/Standard 지원)
        if 'center' in s: X_scaled = (X - np.array(s['center'])) / np.array(s['scale'])
        else: X_scaled = (X - np.array(s.get('mean', 0))) / np.array(s.get('std', s.get('scale', 1)))

        model = DifficultyNet(len(s['cols']))
        model.load_state_dict(torch.load('${join(workingDir, 'model.pth')}', map_location='cpu'))
        model.eval()

        with torch.no_grad():
            input_tensor = torch.from_numpy(X_scaled).float()
            pred = model(input_tensor) * 12 + 0.5
        print(float(pred.item()))
    except Exception as e:
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__": predict()
`;

    const tempPyPath = join(workingDir, `inference_${Date.now()}.py`);
    writeFileSync(tempPyPath, pythonScript);

    try {
        const result = execSync(`python3 ${tempPyPath}`).toString().trim();
        return parseFloat(result);
    } finally {
        if (existsSync(tempFactorPath)) unlinkSync(tempFactorPath);
        if (existsSync(tempPyPath)) unlinkSync(tempPyPath);
    }
}

function main() {
    const [workingDir, tjaPath, difficulty = 'oni'] = process.argv.slice(2);
    
    if (!workingDir || !tjaPath) {
        console.log("Usage: bun run script/predict.ts <workingDir> <tjaPath> [difficulty]");
        process.exit(1);
    }

    const tjaContent = readFileSync(tjaPath, 'utf-8');
    const parsed = parseTja(tjaContent);
    const chart = parsed[difficulty.toLowerCase() as 'oni' | 'edit'];

    if (!chart) {
        console.error(`Error: Difficulty '${difficulty}' not found.`);
        process.exit(1);
    }
    
    const score = runInference(workingDir, factorize(chart));
    console.log(`\n🎯 Predicted Difficulty: ${score.toFixed(2)}`);
}

main();
