const db = require("../config/db");
const Notification = require("../utils/notification.js");

class StandService {
    getStand(id) {
        return new Promise((res, rej) => {
            const sql = `SELECT * FROM stand_list WHERE id = ?`;

            db.get(sql, [id], (err, row) => {
                if (err) {
                    console.error(err.message);
                    return res(false);
                }
                if (!row) {
                    console.log({ error: "id not found" });
                    return res(false);
                }


                let data = {
                    "id": Number(row.id),
                    "title": row.title,
                    "geo": JSON.parse(row.geo),
                    "hour_start": Number(row.hour_start),
                    "hour_end": Number(row.hour_end),
                    "quantity_sheep": Number(row.quantity_sheep),
                    "week_days": JSON.parse(row.week_days),
                    "processing_time": Number(row.processing_time),
                    "status": row.status == 1 ? true : false,
                    "updated_at": Number(row.updated_at),
                    "created_at": Number(row.created_at)
                }

                return res(data);
            });
        });
    }

    getList() {
        return new Promise((res, rej) => {
            const sql = `
                SELECT 
                    *
                FROM 
                    stand_list
                ORDER BY 
                    id
            `;

            db.all(sql, (err, rows) => {
                if (err) {
                    console.error(err.message);
                    return res(false);
                } else {
                    let data = rows.map((row) => {
                        return {
                            "id": Number(row.id),
                            "title": row.title,
                            "geo": JSON.parse(row.geo),
                            "hour_start": Number(row.hour_start),
                            "hour_end": Number(row.hour_end),
                            "quantity_sheep": Number(row.quantity_sheep),
                            "week_days": JSON.parse(row.week_days),
                            "processing_time": Number(row.processing_time),
                            "status": row.status == 1 ? true : false,
                            "updated_at": Number(row.updated_at),
                            "created_at": Number(row.created_at)
                        }
                    })

                    return res(data);
                }
            });
        });
    }

    createStand(data) {
        return new Promise((res, rej) => {
            let sql = 'INSERT INTO stand_list(title, geo, hour_start, hour_end, quantity_sheep, week_days, processing_time, status, updated_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';

            db.run(sql, [
                data.title,
                JSON.stringify(data.geo || []),
                Number(data.hour_start) || 9,
                Number(data.hour_end) || 18,
                Number(data.quantity_sheep) || 2,
                JSON.stringify(data.week_days || [1]),
                Number(data.processing_time) || 1,
                1,
                Math.floor(Date.now()),
                Math.floor(Date.now())
            ], function (err) {
                if (err) {
                    console.error(err.message);
                    return res(false);
                } else {
                    res({ "status": "ok", "id": this.lastID });
                }
            });
        });
    }

    updateStand(stand_id, data) {
        return new Promise((res, rej) => {
            const sql = `
            UPDATE stand_list SET
                title = COALESCE(?, title),
                geo = COALESCE(?, geo),
                hour_start = COALESCE(?, hour_start),
                hour_end = COALESCE(?, hour_end),
                quantity_sheep = COALESCE(?, quantity_sheep),
                processing_time = COALESCE(?, processing_time),
                week_days = COALESCE(?, week_days),
                status = COALESCE(?, status),
                updated_at = ?
            WHERE id = ?
        `;

            db.run(sql, [
                data.title ?? null,
                data.geo !== undefined ? JSON.stringify(data.geo) : null,
                data.hour_start !== undefined ? Number(data.hour_start) : null,
                data.hour_end !== undefined ? Number(data.hour_end) : null,
                data.quantity_sheep !== undefined ? Number(data.quantity_sheep) : null,
                data.processing_time !== undefined ? Number(data.processing_time) : null,
                data.week_days !== undefined ? JSON.stringify(data.week_days) : null,
                data.status !== undefined ? (data.status ? 1 : 0) : null,
                Date.now(),
                stand_id
            ], function (err) {
                if (err) {
                    console.error(err.message);
                    return res(false);
                } else if (this.changes === 0) {
                    return res(false);
                } else {
                    res({ status: "ok", id: stand_id });
                }
            });
        });
    }

    deleteStand(stand_id) {
        return new Promise((res, rej) => {
            db.run('DELETE FROM stand_list WHERE id = ?', [stand_id], function (err) {
                if (err) {
                    console.error(err.message);
                    return res(false);
                } else if (this.changes === 0) {
                    return res(false);
                } else {
                    res({ "status": "ok", "id": stand_id });
                }
            });
        });
    }

    createSchedule(stand_id) {
        return new Promise((res, rej) => {
            let date_start;

            const MS_DAY = 24 * 60 * 60 * 1000;

            // Нормализует timestamp из БД: если в секундах — умножает на 1000
            const normalizeTs = (ts) => {
                if (!ts) return null;
                // если строка — привести к числу
                const n = Number(ts);
                if (Number.isNaN(n)) return null;
                // если кажется секундами (меньше ~1e12) — умножаем
                return n < 1e12 ? n * 1000 : n;
            };

            // Возвращает timestamp (ms) следующего понедельника после ts
            const getNextMonday = (ts) => {
                let date = new Date(ts);
                // нормализуем на начало дня
                date.setHours(0, 0, 0, 0);

                // всегда переходить к следующему дню (чтобы гарантированно получить следующий понедельник,
                // даже если ts уже в понедельник)
                date.setDate(date.getDate() + 1);

                // пока не понедельник (в JS: 1 — понедельник)
                while (date.getDay() !== 1) {
                    date.setDate(date.getDate() + 1);
                }
                return date.getTime();
            };

            // 1. Получаем стенд
            db.get(`SELECT * FROM stand_list WHERE id = ?`, [stand_id], (err, stand) => {
                if (err) {
                    console.error(err.message);
                    return res(false);
                }
                if (!stand) {
                    console.log({ error: "id not found" });
                    return res(false);
                }

                try {
                    stand.geo = JSON.parse(stand.geo);
                } catch (e) {
                    stand.geo = stand.geo;
                }
                try {
                    stand.week_days = JSON.parse(stand.week_days);
                } catch (e) {
                    stand.week_days = Array.isArray(stand.week_days) ? stand.week_days : [];
                }

                // 2. Берём последний date из расписания
                db.get(
                    `SELECT MAX(date) AS max_date FROM stand_schedule WHERE stand_id = ?`,
                    [stand.id],
                    (err, row) => {
                        if (err) {
                            console.error(err.message);
                            return res(false);
                        }

                        const normalized = normalizeTs(row && row.max_date ? row.max_date : null);
                        if (normalized && normalized > Date.now()) {
                            date_start = getNextMonday(normalized);
                        } else {
                            date_start = getNextMonday(Date.now());
                        }

                        // 3. Генерация новых записей
                        // вычисляем количество слотов (целое)
                        const stand_length = Math.max(0, Math.floor((stand.hour_end - stand.hour_start) / stand.processing_time));
                        const timestamp = Date.now();
                        const list = [];

                        // Ожидается, что stand.week_days — массив чисел 0..6, где 0 -> Понедельник, 6 -> Воскресенье
                        for (const dayOffsetRaw of stand.week_days) {
                            const dayOffset = Number(dayOffsetRaw);
                            if (!Number.isFinite(dayOffset) || dayOffset < 0 || dayOffset > 6) continue;

                            // dayOffset: 0 => Monday, 6 => Sunday
                            const stand_date = date_start + (dayOffset * MS_DAY);

                            for (let i = 0; i < stand_length; i++) {
                                for (let q = 0; q < (Number(stand.quantity_sheep) || 0); q++) {
                                    list.push([
                                        stand.hour_start + (stand.processing_time * i),
                                        q,
                                        stand_date,
                                        stand.id,
                                        timestamp,
                                        timestamp
                                    ]);
                                }
                            }
                        }

                        if (list.length === 0) {
                            return res({ status: "empty" });
                        }

                        // 4. Массовая вставка
                        const placeholders = list.map(() => "(?, ?, ?, ?, ?, ?)").join(",");
                        const values = list.flat();
                        const insertSQL = `
                            INSERT INTO stand_schedule(hour, number_sheep, date, stand_id, updated_at, created_at)
                            VALUES ${placeholders}
                        `;

                        db.run(insertSQL, values, function (err) {
                            if (err) {
                                console.error(err.message);
                                return res(false);
                            }
                            let text = [
                                `Стенд «${stand.title}» отримав новий графік. Можна сміливо записуватися 🙂`,
                                `Для «${stand.title}» відкрито новий розклад. Хто планував — саме час.`,
                                `Новий графік для «${stand.title}» вже доступний. Обирайте зручний час 👍`,
                                `Стенд «${stand.title}» оновив розклад. Запис розпочато.`,
                                `З’явилися нові дати у «${stand.title}». Встигніть обрати свою 😉`,
                                `«${stand.title}» відкрив новий період запису. Плануємо заздалегідь 🙂`,
                                `Оновлення для «${stand.title}». Додано новий графік.`,
                                `Новий розклад для «${stand.title}» вже чекає на охочих 📋`,
                                `Стенд «${stand.title}» додав нові години для запису ⏰`,
                                `Графік «${stand.title}» поповнено. Можна бронювати час 😊`,
                                `У «${stand.title}» з’явилися нові можливості для запису`,
                                `Свіжий графік для «${stand.title}» уже доступний 🚀`
                            ];

                            let randomMessage = text[Math.floor(Math.random() * text.length)];

                            Notification.sendStand({
                                title: "Додано новий день служіння",
                                body: randomMessage,
                                page: `/stand/card/${stand.id}`
                            });

                            res({ status: "ok", inserted: list.length });
                        });
                    }
                );
            });
        });
    }

    getScheduleList(stand_id) {
        return new Promise((res, rej) => {
            const sql = `
                SELECT 
                    ss.*, 
                    s.name AS sheep_name
                FROM 
                    stand_schedule AS ss
                LEFT JOIN 
                    sheeps AS s
                ON 
                    s.id = ss.sheep_id
                WHERE
                    ss.stand_id = ?
                AND 
                    date(ss.date / 1000, 'unixepoch', 'localtime') >= date('now', 'localtime')
                ORDER BY 
                    ss.id;
            `;

            db.all(sql, [stand_id], (err, rows) => {
                if (err) {
                    console.error(err.message);
                    return res(false);
                } else {
                    let data = rows.map((row) => {
                        return {
                            "id": Number(row.id),
                            "stand_id": Number(row.stand_id),
                            "date": Number(row.date),
                            "hour": Number(row.hour),
                            "sheep_id": Number(row.sheep_id),
                            "sheep_name": row.sheep_name,
                            "number_sheep": Number(row.number_sheep),
                            "updated_at": Number(row.updated_at),
                            "created_at": Number(row.created_at)
                        }
                    })

                    return res(data);
                }
            });
        });
    }

    getScheduleHistory(id) {
        return new Promise((res, rej) => {
            return res({ id });
        });
    }
}

module.exports = new StandService();