// services/scheduleService.js
const sequelize = require('../config/sequelize');
const { Op } = require('sequelize');
const Schedule = require('../models/schedule');
const ScheduleResponseDTO = require('../dtos/ScheduleResponseDTO');

class ScheduleService {
    /**
     * 스케줄 생성 (벌크)
     * @param {object} [transaction] - Sequelize 트랜잭션 객체 -> 미팅방에서 쓰기위해 트랜잭션을 넘겨받는걸 추가 
     */
    async createSchedules({ userId, title, is_fixed, time_indices }, transaction = null) {
        const overlaps = await Schedule.findAll({
            where: {
                user_id: userId,
                time_idx: {
                    [Op.in]: time_indices
                }
            },
            transaction
        });

        if (overlaps.length > 0) {
            throw new Error(`Schedule overlaps at time_idx ${overlaps[0].time_idx}`);
        }

        const scheduleData = time_indices.map(time_idx => ({
            user_id: userId,
            title,
            time_idx: parseInt(time_idx, 10),
            is_fixed
        }));

        try {
            if (!userId || !title || !time_indices) {
                throw new Error('Required parameters missing');
            }
    
            console.log('Creating schedule with data:', {
                userId,
                title,
                is_fixed,
                time_indices
            });

            const createdSchedules = await Schedule.bulkCreate(scheduleData, {
                transaction,
                returning: true,
                validate: true
            });

            return {
                id: createdSchedules[0].id,
                user_id: userId,
                title,
                is_fixed,
                time_indices,
                createdAt: createdSchedules[0].createdAt,
                updatedAt: createdSchedules[0].updatedAt
            };
        } catch (error) {
            throw new Error(`Failed to bulk create schedules: ${error.message}`);
        }
    }

    async getAllSchedules(userId) {
        try {
            const schedules = await Schedule.findAll({
                where: { user_id: userId },
                order: [['time_idx', 'ASC']]
            });

            return ScheduleResponseDTO.groupSchedules(schedules);
        } catch (error) {
            throw new Error(`Failed to fetch schedules: ${error.message}`);
        }
    }

    async updateSchedules(userId, updates, transaction = null) {
        const { originalTitle, title, is_fixed, time_indices } = updates;
        const t = transaction || await sequelize.transaction();

        try {
            // 기존 스케줄 조회
            const [existingSchedule, existingSchedules] = await Promise.all([
                Schedule.findOne({
                    where: {
                        user_id: userId,
                        title: originalTitle
                    },
                    transaction: t
                }),
                Schedule.findAll({
                    attributes: ['time_idx'],
                    where: {
                        user_id: userId,
                        title: originalTitle
                    },
                    transaction: t
                })
            ]);

            if (!existingSchedule) {
                throw new Error('Schedule not found');
            }

            const existingTimeIndices = existingSchedules.map(s => s.time_idx);
            const toDelete = existingTimeIndices.filter(idx => !time_indices.includes(idx));
            const toAdd = time_indices.filter(idx => !existingTimeIndices.includes(idx));

            // 벌크 연산
            const operations = [];

            // 삭제 연산
            if (toDelete.length > 0) {
                operations.push(
                    Schedule.destroy({
                        where: {
                            user_id: userId,
                            title: originalTitle,
                            time_idx: {
                                [Op.in]: toDelete
                            }
                        },
                        transaction: t
                    })
                );
            }

            // 업데이트 연산
            operations.push(
                Schedule.update(
                    { title, is_fixed },
                    {
                        where: {
                            user_id: userId,
                            title: originalTitle
                        },
                        transaction: t
                    }
                )
            );

            // 생성 연산
            if (toAdd.length > 0) {
                operations.push(
                    Schedule.bulkCreate(
                        toAdd.map(time_idx => ({
                            user_id: userId,
                            title,
                            time_idx,
                            is_fixed
                        })),
                        {
                            transaction: t,
                            validate: true
                        }
                    )
                );
            }

            await Promise.all(operations); // 병렬 처리

            if (!transaction) {
                await t.commit();
            }

            return {
                id: existingSchedule.id,
                user_id: userId,
                title,
                is_fixed,
                time_indices,
                createdAt: existingSchedule.createdAt,
                updatedAt: new Date()
            };

        } catch (error) {
            if (!transaction) {
                await t.rollback();
            }
            throw error;
        }
    }

    async deleteSchedules(userId, title, transaction = null) {
        const deletedSchedules = await Schedule.destroy({
            where: {
                user_id: userId,
                title
            },
            transaction
        });

        return { deletedCount: deletedSchedules };
    }

    /**
     * 특정 time_idx로 스케줄 조회
     */
    async getScheduleByTimeIdx(userId, time_idx) {
        // 해당 time_idx의 스케줄 찾기
        const schedules = await Schedule.findAll({
            where: {
                user_id: userId,
                title: {
                    [Op.in]: sequelize.literal(
                        `(SELECT title FROM Schedules WHERE user_id = ${userId} AND time_idx = ${time_idx})`
                    )
                }
            },
            order: [['time_idx', 'ASC']]
        });

        return ScheduleResponseDTO.groupSchedules(schedules)[0];
    }

    async getAllSchedules(userId) {
        try {
            const schedules = await Schedule.findAll({
                where: { user_id: userId },
                order: [['time_idx', 'ASC']]
            });
            return ScheduleResponseDTO.groupSchedules(schedules);
        } catch (error) {
            throw new Error(`Failed to fetch schedules: ${error.message}`);
        }
    }

    async checkScheduleOverlap(userId, time_idx, transaction = null) {
        const overlappingSchedule = await Schedule.findOne({
            where: { user_id: userId, time_idx },
            transaction
        });
        return !!overlappingSchedule;
    }

    async checkScheduleOverlapByTime(userId, time_idx_start, time_idx_end, transaction = null) {
        const overlappingSchedules = await Schedule.findAll({
            where: {
                user_id: userId,
                time_idx: {
                    [Op.between]: [time_idx_start, time_idx_end]
                }
            },
            transaction
        });

        const groupedSchedules = ScheduleResponseDTO.groupSchedules(overlappingSchedules);
        const result = groupedSchedules.length > 0;

        console.log(`checkScheduleOverlapByTime 호출: userId=${userId}, time_idx_start=${time_idx_start}, time_idx_end=${time_idx_end}`);
        console.log(`중복 스케줄: ${JSON.stringify(groupedSchedules)}`);
        console.log(`스케줄 충돌 결과: ${result}`);

        return result;
    }

    async cleanExpiredSchedules() {
        try {
            const deletedCount = await Schedule.destroy({
                where: { is_fixed: false }
            });
            return { deletedCount };
        } catch (error) {
            console.error('Failed to clean expired schedules:', error);
            throw error;
        }
    }
}

module.exports = new ScheduleService();