const express = require('express');
const path = require('path');
const fs = require('fs');
const sqlite3 = require('sqlite3').verbose();
const sharp = require('sharp');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const client = require('prom-client');
const multer = require('multer');
require('dotenv').config();

const config = require('./config');
const { ensureEnvSecret, initDatabase } = require('./modules/database');

ensureEnvSecret('HOTEL777KEY', 'HOTEL777KEY');
ensureEnvSecret('JWT_SECRET', 'JWT_SECRET');

const app = express();
const PORT = process.env.PORT || 3000;
const API_KEY = process.env.HOTEL777KEY;
const ADMIN_LOGIN = process.env.ADMIN_LOGIN;
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
const JWT_SECRET = process.env.JWT_SECRET;
const MONITORING_USER = process.env.MONITORING_USER || 'monitoring';
const MONITORING_PASSWORD = process.env.MONITORING_PASSWORD || 'monitoring123';

const roomsUploadsDir = path.join(__dirname, 'data', 'room_images');
if (!fs.existsSync(roomsUploadsDir)) fs.mkdirSync(roomsUploadsDir, { recursive: true });
const activityUploadsDir = path.join(__dirname, 'data', 'activity_images');
if (!fs.existsSync(activityUploadsDir)) fs.mkdirSync(activityUploadsDir, { recursive: true });
const heroImagesDir = path.join(__dirname, 'data', 'hero_images');
if (!fs.existsSync(heroImagesDir)) fs.mkdirSync(heroImagesDir, { recursive: true });
const heroVideosDir = path.join(__dirname, 'data', 'hero_videos');
if (!fs.existsSync(heroVideosDir)) fs.mkdirSync(heroVideosDir, { recursive: true });

const storage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, roomsUploadsDir),
    filename: (req, file, cb) => {
        const ext = path.extname(file.originalname).toLowerCase();
        const timestamp = Date.now();
        cb(null, `${timestamp}${ext}`);
    }
});

const upload = multer({
    storage,
    limits: { fileSize: 5 * 1024 * 1024 },
    fileFilter: (req, file, cb) => {
        const allowed = ['.jpg', '.jpeg', '.png', '.webp'];
        const ext = path.extname(file.originalname).toLowerCase();
        if (allowed.includes(ext)) cb(null, true);
        else cb(new Error('Только изображения: jpg, jpeg, png, webp'));
    }
});

const activityStorage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, activityUploadsDir),
    filename: (req, file, cb) => {
        const ext = path.extname(file.originalname).toLowerCase();
        const timestamp = Date.now();
        cb(null, `${timestamp}${ext}`);
    }
});

const uploadActivity = multer({
    storage: activityStorage,
    limits: { fileSize: 5 * 1024 * 1024 },
    fileFilter: (req, file, cb) => {
        const allowed = ['.jpg', '.jpeg', '.png', '.webp'];
        const ext = path.extname(file.originalname).toLowerCase();
        if (allowed.includes(ext)) cb(null, true);
        else cb(new Error('Только изображения: jpg, jpeg, png, webp'));
    }
});

const heroImageStorage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, heroImagesDir),
    filename: (req, file, cb) => {
        const ext = path.extname(file.originalname).toLowerCase();
        const timestamp = Date.now();
        cb(null, `${timestamp}${ext}`);
    }
});

const uploadHeroImage = multer({
    storage: heroImageStorage,
    limits: { fileSize: 5 * 1024 * 1024 },
    fileFilter: (req, file, cb) => {
        const allowed = ['.jpg', '.jpeg', '.png', '.webp'];
        const ext = path.extname(file.originalname).toLowerCase();
        if (allowed.includes(ext)) cb(null, true);
        else cb(new Error('Только изображения: jpg, jpeg, png, webp'));
    }
});

const heroVideoStorage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, heroVideosDir),
    filename: (req, file, cb) => {
        const ext = path.extname(file.originalname).toLowerCase();
        const timestamp = Date.now();
        cb(null, `${timestamp}${ext}`);
    }
});

const uploadHeroVideo = multer({
    storage: heroVideoStorage,
    limits: { fileSize: 100 * 1024 * 1024 },
    fileFilter: (req, file, cb) => {
        const allowed = ['.mp4', '.webm', '.mov', '.avi'];
        const ext = path.extname(file.originalname).toLowerCase();
        if (allowed.includes(ext)) cb(null, true);
        else cb(new Error('Только видео: mp4, webm, mov, avi'));
    }
});

if (!JWT_SECRET) {
    console.error('FATAL: JWT_SECRET environment variable not set');
    process.exit(1);
}

if (JWT_SECRET === 'change-this-secret-in-production-min-32-chars') {
    console.warn('WARNING: Using default JWT_SECRET. Change it in production!');
}

const register = new client.Registry();
client.collectDefaultMetrics({ register });

const httpRequestsTotal = new client.Counter({
    name: 'http_requests_total',
    help: 'Total number of HTTP requests',
    labelNames: ['method', 'path', 'status'],
    registers: [register]
});

const httpRequestDuration = new client.Histogram({
    name: 'http_request_duration_seconds',
    help: 'Duration of HTTP requests in seconds',
    labelNames: ['method', 'path', 'status'],
    buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
    registers: [register]
});

const activeConnections = new client.Gauge({
    name: 'active_connections',
    help: 'Number of active connections',
    registers: [register]
});

const dbQueryDuration = new client.Histogram({
    name: 'db_query_duration_seconds',
    help: 'Duration of database queries in seconds',
    labelNames: ['operation'],
    buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1],
    registers: [register]
});

const bookingsTotal = new client.Gauge({
    name: 'bookings_total',
    help: 'Total number of bookings',
    labelNames: ['status'],
    registers: [register]
});

const roomAvailability = new client.Gauge({
    name: 'room_availability',
    help: 'Number of available rooms by type',
    labelNames: ['type'],
    registers: [register]
});

const loginAttempts = new client.Gauge({
    name: 'login_attempts',
    help: 'Number of login attempts',
    registers: [register]
});

if (!API_KEY) {
    console.error('FATAL: HOTEL777KEY environment variable not set');
    process.exit(1);
}

app.use(express.json({ limit: '1mb' }));
const CORS_ORIGIN = process.env.CORS_ORIGIN || '*';
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', CORS_ORIGIN);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  if (req.method === 'OPTIONS') return res.sendStatus(200);
  next();
});
app.use(express.static(path.join(__dirname, 'public')));
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
app.use('/data/room_images', express.static(roomsUploadsDir));
app.use('/data/activity_images', express.static(activityUploadsDir));
app.use('/data/hero_images', express.static(heroImagesDir));
app.use('/data/hero_videos', express.static(heroVideosDir));

app.use((req, res, next) => {
    const start = Date.now();
    activeConnections.inc();
    res.on('finish', () => {
        const duration = (Date.now() - start) / 1000;
        const path = req.route ? req.route.path : req.path;
        httpRequestsTotal.inc({ method: req.method, path: path, status: res.statusCode });
        httpRequestDuration.observe({ method: req.method, path: path, status: res.statusCode }, duration);
        activeConnections.dec();
    });
    next();
});

app.use('/metrics', (req, res, next) => {
    const authHeader = req.headers.authorization;
    if (!authHeader) {
        res.set('WWW-Authenticate', 'Basic realm="Monitoring"');
        return res.status(401).send('Authentication required');
    }
    const auth = Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
    const user = auth[0];
    const pass = auth[1];
    if (user === MONITORING_USER && pass === MONITORING_PASSWORD) {
        res.set('Content-Type', register.contentType);
        register.metrics().then(metrics => res.end(metrics)).catch(err => res.status(500).send(err.message));
    } else {
        res.set('WWW-Authenticate', 'Basic realm="Monitoring"');
        return res.status(401).send('Authentication required');
    }
});

function updateMetrics() {
    db.all(`SELECT status, COUNT(*) as count FROM bookings GROUP BY status`, [], (err, rows) => {
        if (!err && rows) {
            rows.forEach(row => {
                bookingsTotal.set({ status: row.status || 'unknown' }, row.count);
            });
        }
    });
    db.all(`SELECT type, rooms_count FROM rooms WHERE is_active = 1`, [], (err, rows) => {
        if (!err && rows) {
            rows.forEach(row => {
                roomAvailability.set({ type: row.type }, row.rooms_count);
            });
        }
    });
}

setInterval(updateMetrics, 30000);
setTimeout(updateMetrics, 5000);

const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir);
const dbPath = path.join(dataDir, 'bookings.db');
const db = new sqlite3.Database(dbPath);

initDatabase(db);

const modules = {};

const authModule = require('./modules/auth');
const bookingsModule = require('./modules/bookings');
const adminBookingsModule = require('./modules/adminBookings');
const promocodesModule = require('./modules/promocodes');
const roomsModule = require('./modules/rooms');
const usersModule = require('./modules/users');
const settingsModule = require('./modules/settings');
const reviewsModule = require('./modules/reviews');
const translationsModule = require('./modules/translations');
const backupModule = require('./modules/backup');
const seasonalPricesModule = require('./modules/seasonalPrices');
const emailModule = require('./modules/email');
const reportsModule = require('./modules/reports');
const activitiesModule = require('./modules/activities');
const heroModule = require('./modules/hero');
const { runStartupTests } = require('./tests/runStartupTests');

modules.auth = authModule;
modules.bookings = bookingsModule;
modules.promocodes = promocodesModule;
modules.rooms = roomsModule;
modules.users = usersModule;
modules.adminBookings = adminBookingsModule;
modules.settings = settingsModule;
modules.reviews = reviewsModule;
modules.translations = translationsModule;
modules.backup = backupModule;
modules.seasonalPrices = seasonalPricesModule;
modules.email = emailModule;
modules.reports = reportsModule;
modules.activities = activitiesModule;
modules.hero = heroModule;

authModule.init(db, JWT_SECRET);
bookingsModule.init(db);
adminBookingsModule.init(db);
promocodesModule.init(db);
roomsModule.init(db);
usersModule.init(db, bcrypt);
settingsModule.init(db);
reviewsModule.init(db, settingsModule);
backupModule.init(db, dbPath);
seasonalPricesModule.init(db);
emailModule.init(db, settingsModule);
reportsModule.init(db);
activitiesModule.init(db);
heroModule.init(db);

function initDefaultRooms() {
    db.get("SELECT COUNT(*) as count FROM rooms", (err, row) => {
        if (err) return console.error('Check rooms count error:', err);
        if (row.count > 0) return;
        
        const stmt = db.prepare(`INSERT INTO rooms (type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, image_path, extra_beds, extra_bed_price, is_active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)`);
        
        config.DEFAULT_ROOMS.forEach(r => {
            stmt.run(
                r.type,
                r.name,
                r.description,
                r.rooms_count,
                r.area_sqm,
                r.max_guests,
                JSON.stringify(r.furniture || []),
                JSON.stringify(r.amenities || []),
                JSON.stringify(r.floors || []),
                r.price_per_night,
                r.image_path,
                r.extra_beds || 0,
                r.extra_bed_price || 0
            );
        });
        
        stmt.finalize();
        console.log('✅ Default rooms initialized');
    });
}

function syncAdmin() {
    if (!ADMIN_LOGIN || !ADMIN_PASSWORD) {
        console.warn('WARNING: ADMIN_LOGIN or ADMIN_PASSWORD not set, skipping admin sync');
        return;
    }
    const hash = bcrypt.hashSync(ADMIN_PASSWORD, 10);
    db.get(`SELECT id, role FROM users WHERE login = ?`, [ADMIN_LOGIN], (err, row) => {
        if (err) { console.error('Admin sync error:', err); return; }
        if (row) {
            db.run(`UPDATE users SET password_hash = ?, role = 'admin' WHERE login = ?`, [hash, ADMIN_LOGIN], (err) => {
                if (err) console.error('Admin update error:', err);
                else console.log(`✅ Superadmin "${ADMIN_LOGIN}" updated from .env`);
            });
        } else {
            db.run(`INSERT INTO users (login, password_hash, full_name, email, role) VALUES (?, ?, 'Администратор', NULL, 'admin')`,
                [ADMIN_LOGIN, hash], (err) => {
                    if (err) console.error('Admin creation error:', err);
                    else console.log(`✅ Superadmin "${ADMIN_LOGIN}" created from .env`);
                });
        }
    });
}

setTimeout(() => {
    initDefaultRooms();
    syncAdmin();
}, 500);

authModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
bookingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
adminBookingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
promocodesModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
roomsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin, upload);
usersModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
settingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
reviewsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
backupModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
seasonalPricesModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
emailModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
reportsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
activitiesModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin, uploadActivity);
heroModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin, uploadHeroImage, uploadHeroVideo);

app.get('/api/translations/:lang', (req, res) => {
    const lang = req.params.lang;
    const translations = translationsModule.getTranslations(lang);
    if (!translations) {
        return res.status(404).json({ error: 'Language not found' });
    }
    res.json(translations);
});

app.get('/api/countries-cities', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'data', 'countries-cities.js'));
});

app.get('/api/countries', (req, res) => {
    const countriesPath = path.join(__dirname, 'public', 'data', 'countries.js');
    fs.readFile(countriesPath, 'utf8', (err, data) => {
        if (err) return res.status(500).json({ error: 'File not found' });
        const match = data.match(/const\s+COUNTRIES\s*=\s*(\[.*\]);/s);
        if (match) {
            try {
                let jsonStr = match[1];
                jsonStr = jsonStr.replace(/'/g, '"');
                const countries = JSON.parse(jsonStr);
                res.json(countries);
            } catch (e) {
                res.status(500).json({ error: 'Parse error' });
            }
        } else {
            res.status(500).json({ error: 'Parse error' });
        }
    });
});

function safeJsonString(str) {
    return str.replace(/'/g, "'").replace(/\\"/g, '"');
}

app.get('/api/cities/:countryCode', (req, res) => {
    const countryCode = req.params.countryCode;
    
    const citiesPath = path.join(__dirname, 'public', 'data', 'cities', `${countryCode}.js`);
    const majorCitiesPath = path.join(__dirname, 'public', 'data', 'cities', 'major.js');
    const filePath = fs.existsSync(citiesPath) ? citiesPath : (fs.existsSync(majorCitiesPath) ? majorCitiesPath : null);

    if (!filePath) {
        return res.json({ cities: [], popular: [], countryCode });
    }

    try {
        const data = fs.readFileSync(filePath, 'utf8');
        
        let cities = [];
        const arrayMatch = data.match(/const\s+CITIES_\w+\s*=\s*(\[.*?\]);/s);
        if (arrayMatch) {
            try {
                cities = JSON.parse(safeJsonString(arrayMatch[1]));
            } catch (e) {
                const jsMatch = arrayMatch[1].replace(/'/g, '"');
                try {
                    cities = JSON.parse(jsMatch);
                } catch (e2) {
                    console.error('Failed to parse cities:', e2);
                }
            }
        }

        res.json({ cities, popular: [], countryCode });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});



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

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

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

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

async function convertImages() {
    const imgDir = path.join(__dirname, 'public', 'img');
    if (!fs.existsSync(imgDir)) {
        console.log('Папка img не найдена, пропускаем конвертацию.');
        return;
    }
    const files = fs.readdirSync(imgDir);
    for (const file of files) {
        const ext = path.extname(file).toLowerCase();
        if (ext === '.jpg' || ext === '.jpeg' || ext === '.png') {
            const name = path.parse(file).name;
            const webpPath = path.join(imgDir, `${name}.webp`);
            if (!fs.existsSync(webpPath)) {
                try {
                    await sharp(path.join(imgDir, file))
                        .webp({ quality: 85 })
                        .toFile(webpPath);
                    console.log(`✅ Сконвертировано: ${file} -> ${name}.webp`);
                } catch (err) {
                    console.error(`❌ Ошибка при конвертации ${file}:`, err);
                }
            }
        }
    }
}

convertImages().then(() => {
    const server = app.listen(PORT, async () => {
        console.log('✅ HOTEL777KEY is set');
        console.log('📊 Prometheus metrics available at: http://localhost:' + PORT + '/metrics');
        const monitoringSet = MONITORING_PASSWORD ? 'configured' : 'NOT SET';
        console.log('🔐 Monitoring auth: ' + monitoringSet);
        console.log('');
        await runStartupTests(db, modules);
        console.log(`✅ Hotel 777 server running on http://localhost:${PORT}`);
    });

    function gracefulShutdown(signal) {
        console.log(`\n${signal} received. Shutting down gracefully...`);
        server.close(() => {
            db.close((err) => {
                if (err) {
                    console.error('Error closing database:', err);
                } else {
                    console.log('Database connection closed.');
                }
                console.log('Server closed.');
                process.exit(0);
            });
        });

        setTimeout(() => {
            console.error('Forced shutdown after timeout.');
            process.exit(1);
        }, 10000);
    }

    process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
    process.on('SIGINT', () => gracefulShutdown('SIGINT'));
});
