// server.js

// 1. 모듈 불러오기
const express = require('express');
const path = require('path');
const { spawn } = require('child_process');
const multer = require('multer');
const fs = require('fs');

// 2. Express 앱 생성
const app = express();
const port = 3000;

// ========================================================
// [설정] 경로 및 폴더 초기화
// ========================================================

// AI 모델용 업로드 경로
const uploadPath = '/mnt/user_data/applications/misc/networks/cuuva';
fs.mkdirSync(uploadPath, { recursive: true });

// 관심 인물(POI) 폴더 경로 (vip_tracks)
const vipTracksPath = '/mnt/user_data/applications/misc/vip_tracks';
fs.mkdirSync(vipTracksPath, { recursive: true });


// ========================================================
// [설정] Multer (파일 업로드 핸들러)
// ========================================================

// 1) AI 모델용 스토리지
const modelStorage = multer.diskStorage({
    destination: (req, file, cb) => { cb(null, uploadPath); },
    filename: (req, file, cb) => { cb(null, file.originalname); }
});

const modelFileFilter = (req, file, cb) => {
    if (!file.originalname.endsWith('.aiwbin')) {
        return cb(new Error('Invalid file type: Only .aiwbin files are allowed'), false);
    }
    cb(null, true);
};

const uploadModel = multer({ storage: modelStorage, fileFilter: modelFileFilter });


// 2) 관심 인물(POI) 이미지용 스토리지
const poiStorage = multer.diskStorage({
    destination: (req, file, cb) => { cb(null, vipTracksPath); },
    filename: (req, file, cb) => {
        // 임시 저장을 위해 충돌 없는 이름을 생성
        const ext = path.extname(file.originalname);
        cb(null, `temp_${Date.now()}_${Math.round(Math.random() * 1E9)}${ext}`);
    }
});

const poiFileFilter = (req, file, cb) => {
    const ext = path.extname(file.originalname).toLowerCase();
    if (!['.jpg', '.jpeg', '.png'].includes(ext)) {
        return cb(new Error('이미지 파일만 업로드 가능합니다 (.jpg, .png)'), false);
    }
    cb(null, true);
};

const uploadPoi = multer({ storage: poiStorage, fileFilter: poiFileFilter });


// ========================================================
// [미들웨어 및 라우트 설정]
// ========================================================

app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());

// POI 이미지 서빙
app.get('/poi-images/:folder/:filename', (req, res) => {
    const { folder, filename } = req.params;

    if (folder.includes('..') || filename.includes('..')) {
        return res.status(400).send('Invalid path');
    }

    const filePath = path.join(vipTracksPath, folder, filename);

    if (fs.existsSync(filePath)) {
        res.sendFile(filePath);
    } else {
        res.status(404).send('Image not found');
    }
});

// 페이지 라우팅
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.get('/dashboard', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'dashboard.html'));
});


// ========================================================
// [API] AI 모델 관련
// ========================================================

app.post('/set-model', (req, res) => {
    const { model } = req.body;
    if (!model) return res.status(400).json({ status: 'error', message: '모델 값이 없습니다.' });

    const scriptCommand = '/mnt/user_data/feat_control/feat_on.sh';
    const args = [model];
    const scriptProcess = spawn(scriptCommand, args);

    let stdoutData = '';
    scriptProcess.stdout.on('data', (data) => { stdoutData += data.toString(); });

    scriptProcess.on('close', (code) => {
        if (code === 0) {
            res.json({ status: 'success', message: `모델이 ${model}(으)로 변경됨`, output: stdoutData });
        } else {
            res.status(500).json({ status: 'error', message: '쉘 스크립트 실행 실패' });
        }
    });
});

app.post('/upload-model', uploadModel.single('modelFile'), (req, res) => {
    if (!req.file) return res.status(400).json({ status: 'error', message: '파일 없음' });
    res.json({ status: 'success', message: '업로드 성공', filePath: req.file.path });
});

function isVersionGreater(v1, v2) {
    if (!v2) return true;
    const parts1 = v1.replace('v', '').split('.').map(Number);
    const parts2 = v2.replace('v', '').split('.').map(Number);
    if (parts1[0] > parts2[0]) return true;
    if (parts1[0] < parts2[0]) return false;
    if (parts1[1] > parts2[1]) return true;
    return false;
}

app.get('/list-models', (req, res) => {
    const modelRegex = /^CUUVA_([A-Z]+)_v(\d+\.\d+)\.aiwbin$/;
    const latestModels = {};

    fs.readdir(uploadPath, (err, files) => {
        if (err) return res.status(500).json({ status: 'error' });
        files.forEach(file => {
            const match = file.match(modelRegex);
            if (match) {
                const role = match[1];
                const version = `v${match[2]}`;
                const existing = latestModels[role];
                if (!existing || isVersionGreater(version, existing.version)) {
                    latestModels[role] = { file: file, version: version };
                }
            }
        });
        res.json(latestModels);
    });
});

app.post('/delete-model', (req, res) => {
    const { filename } = req.body;
    if (!filename) return res.status(400).json({ status: 'error', message: '파일명 누락' });
    const safeFilename = path.basename(filename);
    if (!safeFilename.endsWith('.aiwbin')) return res.status(400).json({ status: 'error', message: '잘못된 파일' });
    const filePath = path.join(uploadPath, safeFilename);
    fs.unlink(filePath, (err) => {
        if (err && err.code !== 'ENOENT') return res.status(500).json({ status: 'error' });
        res.json({ status: 'success', message: '삭제 완료' });
    });
});


// ========================================================
// [API] 관심 인물(POI) 관리 관련
// ========================================================

app.get('/poi/list', (req, res) => {
    fs.readdir(vipTracksPath, { withFileTypes: true }, (err, entries) => {
        if (err) return res.json([]);

        const result = entries
            .filter(dirent => dirent.isDirectory())
            .map(dirent => {
                const name = dirent.name;
                const dirPath = path.join(vipTracksPath, name);
                let imageFiles = [];

                try {
                    const files = fs.readdirSync(dirPath);
                    imageFiles = files.filter(file => /\.(jpg|jpeg|png)$/i.test(file));
                } catch (e) {
                    imageFiles = [];
                }

                return { name, images: imageFiles };
            });

        res.json(result);
    });
});

app.post('/poi/register', (req, res) => {
    const { name } = req.body;
    const nameRegex = /^[a-zA-Z0-9]+$/;

    if (!name || !nameRegex.test(name)) {
        return res.status(400).json({ status: 'error', message: '이름은 영문과 숫자만 가능합니다.' });
    }
    const targetDir = path.join(vipTracksPath, name);
    if (fs.existsSync(targetDir)) {
        return res.status(400).json({ status: 'error', message: '이미 등록된 이름입니다.' });
    }
    try {
        fs.mkdirSync(targetDir);
        res.json({ status: 'success' });
    } catch (err) {
        res.status(500).json({ status: 'error', message: '폴더 생성 실패' });
    }
});

app.post('/poi/delete', (req, res) => {
    const { name } = req.body;
    const safeName = path.basename(name);
    const targetDir = path.join(vipTracksPath, safeName);

    try {
        if (fs.existsSync(targetDir)) {
            fs.rmSync(targetDir, { recursive: true, force: true });
        }
        res.json({ status: 'success' });
    } catch (err) {
        res.status(500).json({ status: 'error', message: '삭제 실패' });
    }
});

app.post('/poi/delete-image', (req, res) => {
    const { name, imageName } = req.body;
    if (!name || !imageName) return res.status(400).json({ status: 'error', message: '정보 누락' });
    const safeName = path.basename(name);
    const safeImageName = path.basename(imageName);
    const targetFile = path.join(vipTracksPath, safeName, safeImageName);

    if (fs.existsSync(targetFile)) {
        try {
            fs.unlinkSync(targetFile);
            res.json({ status: 'success', message: '이미지가 삭제되었습니다.' });
        } catch (err) {
            res.status(500).json({ status: 'error', message: '파일 삭제 오류' });
        }
    } else {
        res.status(404).json({ status: 'error', message: '이미지가 없습니다.' });
    }
});

// [수정됨] 다중 파일 업로드 처리 (uploadPoi.array 사용)
app.post('/poi/upload-image', uploadPoi.array('poiFile'), (req, res) => {
    // 다중 파일인 경우 req.files에 배열로 들어옴
    if (!req.files || req.files.length === 0) {
        return res.status(400).json({ status: 'error', message: '파일 업로드 실패 (jpg, png만 가능)' });
    }

    const poiName = req.body.poiName;

    // poiName이 없으면 임시 파일 모두 삭제
    if (!poiName) {
        req.files.forEach(file => {
            if(fs.existsSync(file.path)) fs.unlinkSync(file.path);
        });
        return res.status(400).json({ status: 'error', message: '대상 인물 정보 누락' });
    }

    const targetDir = path.join(vipTracksPath, poiName);

    try {
        if (!fs.existsSync(targetDir)) {
            fs.mkdirSync(targetDir);
        }

        // 업로드된 모든 파일을 순회하며 이동
        req.files.forEach(file => {
            const ext = path.extname(file.originalname).toLowerCase();
            // 파일명 중복 방지를 위해 난수 추가
            const newFileName = `img_${Date.now()}_${Math.floor(Math.random() * 100000)}${ext}`;
            const targetPath = path.join(targetDir, newFileName);

            fs.renameSync(file.path, targetPath);
        });

        res.json({ status: 'success', message: '이미지 등록 완료' });

    } catch (err) {
        console.error(err);
        // 에러 발생 시 남아있는 임시 파일 정리
        req.files.forEach(file => {
            if(fs.existsSync(file.path)) try { fs.unlinkSync(file.path); } catch(e){}
        });
        res.status(500).json({ status: 'error', message: '파일 이동 실패' });
    }
});

// 서버 실행
app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}`);
});