const config = require('../../config');
const { validatePromocode } = require('../bookings');

const promocodeRateLimit = new Map();
const PROMOCODE_WINDOW = 60 * 1000;
const MAX_PROMOCODE_REQUESTS = 20;

function checkPromocodeRateLimit(ip) {
    const now = Date.now();
    const record = promocodeRateLimit.get(ip);
    if (!record) {
        promocodeRateLimit.set(ip, { count: 1, firstRequest: now });
        return true;
    }
    if (now - record.firstRequest > PROMOCODE_WINDOW) {
        promocodeRateLimit.set(ip, { count: 1, firstRequest: now });
        return true;
    }
    if (record.count >= MAX_PROMOCODE_REQUESTS) {
        return false;
    }
    record.count++;
    return true;
}

setInterval(() => {
    const now = Date.now();
    for (const [ip, record] of promocodeRateLimit.entries()) {
        if (now - record.firstRequest > PROMOCODE_WINDOW) {
            promocodeRateLimit.delete(ip);
        }
    }
}, PROMOCODE_WINDOW);

let db;

function init(database) {
    db = database;
}

function getBookingsForAdmin(req, res) {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 20;
    const offset = (page - 1) * limit;
    const search = req.query.search || '';
    const statusFilter = req.query.status || '';

    let whereClause = '1=1';
    const params = [];

    if (search) {
        whereClause += ' AND (b.name LIKE ? OR b.phone LIKE ? OR b.room_type LIKE ? OR r.name LIKE ?)';
        const searchTerm = `%${search}%`;
        params.push(searchTerm, searchTerm, searchTerm, searchTerm);
    }

    if (statusFilter && statusFilter !== 'all') {
        whereClause += ' AND b.status = ?';
        params.push(statusFilter);
    }

    db.get(`SELECT COUNT(*) as total FROM bookings b LEFT JOIN rooms r ON b.room_id = r.id WHERE ${whereClause}`, params, (err, countRow) => {
        if (err) {
            console.error(err);
            return res.status(500).json({ error: 'Database error' });
        }

        const total = countRow.total;
        const totalPages = Math.ceil(total / limit);

        db.all(`SELECT b.*, p.code as promocode_code, r.name as room_name, r.type as room_type
                FROM bookings b
                LEFT JOIN promocodes p ON b.promocode_id = p.id
                LEFT JOIN rooms r ON b.room_id = r.id
                WHERE ${whereClause}
                ORDER BY b.checkin_date ASC
                LIMIT ? OFFSET ?`, [...params, limit, offset], (err, rows) => {
            if (err) {
                console.error(err);
                return res.status(500).json({ error: 'Database error' });
            }
            res.json({
                data: rows,
                pagination: {
                    page,
                    limit,
                    total,
                    totalPages
                }
            });
        });
    });
}

function updateBookingStatus(req, res) {
    const bookingId = parseInt(req.params.id);
    const { status } = req.body;
    const validStatuses = config.STATUS_LIST;
    if (!status || !validStatuses.includes(status)) {
        return res.status(400).json({ error: 'Invalid status. Valid: ' + validStatuses.join(', ') });
    }
    db.get(`SELECT status FROM bookings WHERE id = ?`, [bookingId], (err, row) => {
        if (err) return res.status(500).json({ error: 'Database error' });
        if (!row) return res.status(404).json({ error: 'Booking not found' });
        const oldValue = row.status;
        db.run(`UPDATE bookings SET status = ? WHERE id = ?`, [status, bookingId], (err) => {
            if (err) return res.status(500).json({ error: 'Database error' });
            logHistory(bookingId, req.user.id, req.user.login, 'status', oldValue, status);
            db.get(`SELECT b.*, p.code as promocode_code, r.name as room_name, r.type as room_type
                    FROM bookings b
                    LEFT JOIN promocodes p ON b.promocode_id = p.id
                    LEFT JOIN rooms r ON b.room_id = r.id
                    WHERE b.id = ?`, [bookingId], (err, row) => {
                if (err) return res.status(500).json({ error: 'Database error' });
                try { require('../email').sendStatusChange(row, oldValue, status); } catch {}
                res.json({ message: 'Status updated', booking: row });
            });
        });
    });
}

function updateBookingRoom(req, res) {
    const bookingId = parseInt(req.params.id);
    const { room_id } = req.body;
    
    db.get(`SELECT * FROM bookings WHERE id = ?`, [bookingId], (err, booking) => {
        if (err) return res.status(500).json({ error: 'Database error' });
        if (!booking) return res.status(404).json({ error: 'Booking not found' });
        
        if (!room_id) {
            return res.status(400).json({ error: 'room_id is required' });
        }
        
        db.get(`SELECT * FROM rooms WHERE id = ?`, [room_id], (err, room) => {
            if (err) return res.status(500).json({ error: 'Database error' });
            if (!room) return res.status(400).json({ error: 'Room not found' });
            
            const oldValue = booking.room_name ? booking.room_type + ' — ' + booking.room_name : booking.room_type || 'Не указан';
            const newValue = room.type + ' — ' + room.name;
            const totalGuests = (booking.adults || 0) + (booking.children || 0);
            const basePrice = config.calculateBasePrice(room.type, booking.checkin_date, booking.checkout_date) * Math.max(1, totalGuests);
            const discountAmount = Math.round(basePrice * (booking.discount_percent || 0) / 100);
            const totalPrice = basePrice - discountAmount;
            
            db.run(`UPDATE bookings SET room_id = ?, room_type = ?, base_price = ?, discount_amount = ?, total_price = ? WHERE id = ?`,
                [room.id, room.type, basePrice, discountAmount, totalPrice, bookingId], (err) => {
                if (err) return res.status(500).json({ error: 'Database error' });
                logHistory(bookingId, req.user.id, req.user.login, 'room', oldValue, newValue);
                db.get(`SELECT b.*, p.code as promocode_code, r.name as room_name, r.type as room_type
                        FROM bookings b
                        LEFT JOIN promocodes p ON b.promocode_id = p.id
                        LEFT JOIN rooms r ON b.room_id = r.id
                        WHERE b.id = ?`, [bookingId], (err, row) => {
                    if (err) return res.status(500).json({ error: 'Database error' });
                    res.json({ message: 'Room updated', booking: row });
                });
            });
        });
    });
}

function updateBookingComment(req, res) {
    const bookingId = parseInt(req.params.id);
    const { comment } = req.body;
    db.get(`SELECT comment FROM bookings WHERE id = ?`, [bookingId], (err, row) => {
        if (err) return res.status(500).json({ error: 'Database error' });
        if (!row) return res.status(404).json({ error: 'Booking not found' });
        const oldValue = row.comment || 'Нет';
        db.run(`UPDATE bookings SET comment = ? WHERE id = ?`, [comment || null, bookingId], (err) => {
            if (err) return res.status(500).json({ error: 'Database error' });
            logHistory(bookingId, req.user.id, req.user.login, 'comment', oldValue, comment || 'Нет');
            db.get(`SELECT b.*, p.code as promocode_code, r.name as room_name, r.type as room_type
                    FROM bookings b
                    LEFT JOIN promocodes p ON b.promocode_id = p.id
                    LEFT JOIN rooms r ON b.room_id = r.id
                    WHERE b.id = ?`, [bookingId], (err, row) => {
                if (err) return res.status(500).json({ error: 'Database error' });
                res.json({ message: 'Comment updated', booking: row });
            });
        });
    });
}

function updateBookingDiscount(req, res) {
    const bookingId = parseInt(req.params.id);
    const { discount_percent } = req.body;
    if (discount_percent === undefined || discount_percent < 0 || discount_percent > 99) {
        return res.status(400).json({ error: 'Discount percent must be between 0 and 99' });
    }
    db.get(`SELECT base_price, discount_percent FROM bookings WHERE id = ?`, [bookingId], (err, row) => {
        if (err) return res.status(500).json({ error: 'Database error' });
        if (!row) return res.status(404).json({ error: 'Booking not found' });
        const oldValue = row.discount_percent || 0;
        const basePrice = row.base_price || 0;
        const discountAmount = Math.round(basePrice * discount_percent / 100);
        const totalPrice = basePrice - discountAmount;
        db.run(`UPDATE bookings SET discount_percent = ?, discount_amount = ?, total_price = ? WHERE id = ?`,
            [discount_percent, discountAmount, totalPrice, bookingId], (err) => {
            if (err) return res.status(500).json({ error: 'Database error' });
            logHistory(bookingId, req.user.id, req.user.login, 'discount_percent', oldValue.toString(), discount_percent.toString());
            db.get(`SELECT b.*, p.code as promocode_code, r.name as room_name, r.type as room_type
                    FROM bookings b
                    LEFT JOIN promocodes p ON b.promocode_id = p.id
                    LEFT JOIN rooms r ON b.room_id = r.id
                    WHERE b.id = ?`, [bookingId], (err, row) => {
                    if (err) return res.status(500).json({ error: 'Database error' });
                    res.json({ message: 'Discount updated', booking: row });
                });
        });
    });
}

function updateBookingDetails(req, res) {
    const bookingId = parseInt(req.params.id);
    const { adults, children, checkin_date, checkout_date } = req.body;
    if (adults === undefined && children === undefined && checkin_date === undefined && checkout_date === undefined) {
        return res.status(400).json({ error: 'No fields to update' });
    }

    const newAdults = adults !== undefined ? parseInt(adults) : undefined;
    const newChildren = children !== undefined ? parseInt(children) : undefined;
    const newCheckin = checkin_date || undefined;
    const newCheckout = checkout_date || undefined;

    if (newAdults !== undefined && (isNaN(newAdults) || newAdults < 1)) {
        return res.status(400).json({ error: 'Количество взрослых должно быть минимум 1' });
    }
    if (newChildren !== undefined && (isNaN(newChildren) || newChildren < 0)) {
        return res.status(400).json({ error: 'Количество детей не может быть отрицательным' });
    }

    const checkinDate = newCheckin ? new Date(newCheckin) : new Date();
    const checkoutDate = newCheckout ? new Date(newCheckout) : new Date();

    if (newCheckin && isNaN(checkinDate.getTime())) {
        return res.status(400).json({ error: 'Некорректная дата заезда' });
    }
    if (newCheckout && isNaN(checkoutDate.getTime())) {
        return res.status(400).json({ error: 'Некорректная дата выезда' });
    }
    if (newCheckin && newCheckout && checkoutDate <= checkinDate) {
        return res.status(400).json({ error: 'Дата выезда должна быть позже даты заезда' });
    }

    db.get(`SELECT * FROM bookings WHERE id = ?`, [bookingId], (err, row) => {
        if (err) return res.status(500).json({ error: 'Database error' });
        if (!row) return res.status(404).json({ error: 'Booking not found' });
        const oldValues = {
            adults: row.adults,
            children: row.children,
            checkin_date: row.checkin_date,
            checkout_date: row.checkout_date
        };
        let fields = [];
        let values = [];
        if (newAdults !== undefined) { fields.push('adults = ?'); values.push(newAdults); }
        if (newChildren !== undefined) { fields.push('children = ?'); values.push(newChildren); }
        if (newCheckin !== undefined) { fields.push('checkin_date = ?'); values.push(newCheckin); }
        if (newCheckout !== undefined) { fields.push('checkout_date = ?'); values.push(newCheckout); }
        if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' });
        values.push(bookingId);
        db.run(`UPDATE bookings SET ${fields.join(', ')} WHERE id = ?`, values, function(err) {
            if (err) return res.status(500).json({ error: 'Database error' });
            if (adults !== undefined && adults !== oldValues.adults) {
                logHistory(bookingId, req.user.id, req.user.login, 'adults', oldValues.adults.toString(), adults.toString());
            }
            if (children !== undefined && children !== oldValues.children) {
                logHistory(bookingId, req.user.id, req.user.login, 'children', oldValues.children.toString(), children.toString());
            }
            if (checkin_date !== undefined && checkin_date !== oldValues.checkin_date) {
                logHistory(bookingId, req.user.id, req.user.login, 'checkin_date', oldValues.checkin_date, checkin_date);
            }
            if (checkout_date !== undefined && checkout_date !== oldValues.checkout_date) {
                logHistory(bookingId, req.user.id, req.user.login, 'checkout_date', oldValues.checkout_date, checkout_date);
            }
            const finalAdults = adults !== undefined ? adults : oldValues.adults;
            const finalChildren = children !== undefined ? children : oldValues.children;
            const finalCheckin = checkin_date !== undefined ? checkin_date : oldValues.checkin_date;
            const finalCheckout = checkout_date !== undefined ? checkout_date : oldValues.checkout_date;
            const totalGuests = finalAdults + finalChildren;
            const roomType = row.room_type;
            if (!roomType) {
                return finishUpdate();
            }
            db.get(`SELECT price_per_night FROM rooms WHERE type = ? AND is_active = 1`, [roomType], (err, roomData) => {
                if (err) return res.status(500).json({ error: 'Database error' });
                const pricePerNight = roomData ? roomData.price_per_night : config.getRoomPrice(roomType);
                const nights = config.calculateNights(finalCheckin, finalCheckout);
                const basePrice = pricePerNight * totalGuests * nights;
                const discountPercent = row.discount_percent || 0;
                const discountAmount = Math.round(basePrice * discountPercent / 100);
                const totalPrice = basePrice - discountAmount;
                db.run(`UPDATE bookings SET base_price = ?, discount_amount = ?, total_price = ? WHERE id = ?`,
                    [basePrice, discountAmount, totalPrice, bookingId], (err) => {
                        if (err) return res.status(500).json({ error: 'Database error' });
                        finishUpdate();
                    });
            });
            function finishUpdate() {
                db.get(`SELECT b.*, p.code as promocode_code, r.name as room_name, r.type as room_type
                        FROM bookings b
                        LEFT JOIN promocodes p ON b.promocode_id = p.id
                        LEFT JOIN rooms r ON b.room_id = r.id
                        WHERE b.id = ?`, [bookingId], (err, row) => {
                    if (err) return res.status(500).json({ error: 'Database error' });
                    res.json({ message: 'Booking details updated', booking: row });
                });
            }
        });
    });
}

function logHistory(bookingId, userId, userLogin, field, oldValue, newValue) {
    db.run(`INSERT INTO booking_history (booking_id, user_id, user_login, field, old_value, new_value) VALUES (?, ?, ?, ?, ?, ?)`,
        [bookingId, userId, userLogin, field, oldValue, newValue], (err) => {
            if (err) console.error('History log error:', err);
        });
}

function validatePromocodeAPI(req, res) {
    const clientIp = req.ip || req.connection.remoteAddress || 'unknown';
    
    if (!checkPromocodeRateLimit(clientIp)) {
        return res.status(429).json({ error: 'Too many requests. Please try again later.' });
    }
    
    const { code, room_type, checkin, checkout, guests } = req.body;
    if (!code) return res.status(400).json({ error: 'Promocode required' });
    validatePromocode(code, (err, promo) => {
        if (err) return res.status(500).json({ error: 'Database error' });
        if (!promo) return res.status(404).json({ error: 'Invalid or expired promocode' });
        const guestsCount = parseInt(guests) || 1;
        const basePrice = config.calculateBasePrice(room_type, checkin, checkout) * guestsCount;
        const discountAmount = Math.round(basePrice * promo.discount_percent / 100);
        const totalPrice = basePrice - discountAmount;
        res.json({
            valid: true,
            discount_percent: promo.discount_percent,
            base_price: basePrice,
            discount_amount: discountAmount,
            total_price: totalPrice,
            code: promo.code
        });
    });
}

function exportCSV(req, res) {
    const statusFilter = req.query.status || '';
    const from = req.query.from || '';
    const to = req.query.to || '';

    let whereClause = '1=1';
    const params = [];

    if (statusFilter && statusFilter !== 'all') {
        whereClause += ' AND b.status = ?';
        params.push(statusFilter);
    }
    if (from) {
        whereClause += ' AND b.checkin_date >= ?';
        params.push(from);
    }
    if (to) {
        whereClause += ' AND b.checkin_date <= ?';
        params.push(to);
    }

    db.all(`SELECT b.*, p.code as promocode_code FROM bookings b LEFT JOIN promocodes p ON b.promocode_id = p.id WHERE ${whereClause} ORDER BY b.checkin_date ASC`, params, (err, rows) => {
        if (err) return res.status(500).json({ error: 'Database error' });

        const headers = ['ID', 'Имя', 'Телефон', 'Взрослых', 'Детей', 'Заезд', 'Выезд', 'Тип номера', 'Комментарий', 'Базовая цена', 'Скидка %', 'Сумма скидки', 'Итого', 'Промокод', 'Статус', 'Пожелания'];

        function escapeCsv(val) {
            if (val === null || val === undefined) return '';
            const str = String(val);
            if (str.includes(';') || str.includes('"') || str.includes('\n')) {
                return '"' + str.replace(/"/g, '""') + '"';
            }
            return str;
        }

        const csvRows = ['\uFEFF' + headers.join(';')];
        rows.forEach(r => {
            csvRows.push([
                r.id, r.name, r.phone, r.adults, r.children,
                r.checkin_date, r.checkout_date, r.room_type, r.comment,
                r.base_price, r.discount_percent, r.discount_amount, r.total_price,
                r.promocode_code, r.status, r.wishes
            ].map(escapeCsv).join(';'));
        });

        const dateStr = new Date().toISOString().split('T')[0];
        res.setHeader('Content-Type', 'text/csv; charset=utf-8');
        res.setHeader('Content-Disposition', `attachment; filename="bookings_${dateStr}.csv"`);
        res.send(csvRows.join('\n'));
    });
}

function getCalendar(req, res) {
    const month = req.query.month;
    if (!month) return res.status(400).json({ error: 'month parameter required (YYYY-MM)' });

    const startDate = month + '-01';
    const [y, m] = month.split('-').map(Number);
    const endDate = new Date(y, m, 0).toISOString().split('T')[0];

    db.all(`SELECT r.type, r.name, r.id as room_id, r.rooms_count, r.price_per_night, r.image_path FROM rooms r WHERE r.is_active = 1 ORDER BY r.price_per_night ASC`, [], (err, rooms) => {
        if (err) return res.status(500).json({ error: 'Database error' });

        db.all(`SELECT b.id, b.name, b.status, b.room_type, b.room_id, b.checkin_date, b.checkout_date, b.phone
                FROM bookings b
                WHERE b.status NOT IN ('отменена','выехала')
                AND b.checkin_date <= ? AND b.checkout_date > ?
                ORDER BY b.checkin_date ASC`,
            [endDate, startDate], (err, bookings) => {
            if (err) return res.status(500).json({ error: 'Database error' });

            const days = {};
            const currentDate = new Date(startDate);
            const end = new Date(endDate);

            while (currentDate <= end) {
                const dateStr = currentDate.toISOString().split('T')[0];
                days[dateStr] = {};
                rooms.forEach(r => {
                    days[dateStr][r.type] = { total: r.rooms_count, booked: 0, bookings: [] };
                });
                currentDate.setDate(currentDate.getDate() + 1);
            }

            bookings.forEach(b => {
                let d = new Date(Math.max(new Date(b.checkin_date).getTime(), new Date(startDate).getTime()));
                const endD = new Date(Math.min(new Date(b.checkout_date).getTime(), new Date(endDate).getTime()));
                while (d < endD) {
                    const dateStr = d.toISOString().split('T')[0];
                    const type = b.room_type || 'Не указан';
                    if (days[dateStr]) {
                        if (!days[dateStr][type]) {
                            days[dateStr][type] = { total: 0, booked: 0, bookings: [] };
                        }
                        days[dateStr][type].booked++;
                        if (days[dateStr][type].bookings.length < 10) {
                            days[dateStr][type].bookings.push({ id: b.id, name: b.name, status: b.status, phone: b.phone });
                        }
                    }
                    d.setDate(d.getDate() + 1);
                }
            });

            res.json({ month, rooms: rooms.map(r => ({ type: r.type, name: r.name, id: r.room_id, rooms_count: r.rooms_count, price_per_night: r.price_per_night })), days });
        });
    });
}

function setupRoutes(app, authenticateToken, requireAdmin) {
    app.get('/api/admin/bookings', authenticateToken, requireAdmin, getBookingsForAdmin);
    app.patch('/api/admin/bookings/:id', authenticateToken, requireAdmin, updateBookingStatus);
    app.patch('/api/admin/bookings/:id/room', authenticateToken, requireAdmin, updateBookingRoom);
    app.patch('/api/admin/bookings/:id/comment', authenticateToken, requireAdmin, updateBookingComment);
    app.patch('/api/admin/bookings/:id/discount', authenticateToken, requireAdmin, updateBookingDiscount);
    app.patch('/api/admin/bookings/:id/details', authenticateToken, requireAdmin, updateBookingDetails);
    app.post('/api/promocodes/validate', validatePromocodeAPI);
    app.get('/api/admin/export/bookings', authenticateToken, exportCSV);
    app.get('/api/admin/calendar', authenticateToken, getCalendar);
}

module.exports = { init, setupRoutes };
