const { Schedule, Route, Vehicle, Booking, ScheduleStop, Stop } = require('../models');
const { Op } = require('sequelize');
const { notifyNewTrip } = require('../services/PushNotificationService');
const path = require('path');
const fs = require('fs');

class ScheduleController {
  async getUpcoming(req, res, next) {
    try {
      const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
      const today = days[new Date().getDay()];
      const todayIndex = days.indexOf(today);

      const upcomingDays = days.slice(todayIndex).concat(days.slice(0, todayIndex));

      const schedules = await Schedule.findAll({
        where: { is_active: true },
        include: [
          { model: Route, where: { is_active: true } },
          { model: Vehicle, where: { is_active: true } },
          { model: ScheduleStop, include: [{ model: Stop }] },
        ],
        order: [
          [Route, 'origin', 'ASC'],
          ['departure_day', 'ASC'],
          ['departure_time', 'ASC'],
        ],
      });

      const schedulesWithAvailability = await Promise.all(
        schedules.map(async (s) => {
          const sched = s.toJSON();
          const bookedSeats = await Booking.sum('number_of_passengers', {
            where: {
              schedule_id: s.id,
              booking_status: { [Op.notIn]: ['cancelled', 'completed'] },
            },
          });
          sched.available_seats = (s.max_seats || s.vehicle.capacity) - (bookedSeats || 0);
          return sched;
        })
      );

      res.json({ schedules: schedulesWithAvailability });
    } catch (error) {
      next(error);
    }
  }

  async getById(req, res, next) {
    try {
      const schedule = await Schedule.findByPk(req.params.id, {
        include: [
          { model: Route },
          { model: Vehicle },
          { model: ScheduleStop, include: [{ model: Stop }] },
        ],
      });
      if (!schedule) return res.status(404).json({ error: 'Schedule not found' });

      const bookedSeats = await Booking.sum('number_of_passengers', {
        where: {
          schedule_id: schedule.id,
          booking_status: { [Op.notIn]: ['cancelled', 'completed'] },
        },
      });

      const sched = schedule.toJSON();
      sched.available_seats = (schedule.max_seats || schedule.vehicle.capacity) - (bookedSeats || 0);
      res.json({ schedule: sched });
    } catch (error) {
      next(error);
    }
  }

  async getAll(req, res, next) {
    try {
      const schedules = await Schedule.findAll({
        include: [
          { model: Route },
          { model: Vehicle },
          { model: ScheduleStop, include: [{ model: Stop }] },
        ],
        order: [['departure_day', 'ASC'], ['departure_time', 'ASC']],
      });

      const withAvailability = await Promise.all(
        schedules.map(async (s) => {
          const sched = s.toJSON();
          const bookedSeats = await Booking.sum('number_of_passengers', {
            where: {
              schedule_id: s.id,
              booking_status: { [Op.notIn]: ['cancelled', 'completed'] },
            },
          });
          const capacity = s.max_seats || s.vehicle.capacity;
          sched.available_seats = capacity - (bookedSeats || 0);
          sched.capacity = capacity;
          return sched;
        })
      );

      res.json({ schedules: withAvailability });
    } catch (error) {
      next(error);
    }
  }

  async create(req, res, next) {
    try {
      const { stopIds, ...scheduleData } = req.body;
      const schedule = await Schedule.create(scheduleData);

      if (stopIds && Array.isArray(stopIds) && stopIds.length > 0) {
        await ScheduleStop.bulkCreate(
          stopIds.map((stopId) => ({ schedule_id: schedule.id, stop_id: stopId }))
        );
      }

      const fullSchedule = await Schedule.findByPk(schedule.id, {
        include: [
          { model: Route },
          { model: Vehicle },
          { model: ScheduleStop, include: [{ model: Stop }] },
        ],
      });
      notifyNewTrip(fullSchedule).catch((e) => console.error('[Push] New trip notify error:', e.message));
      res.status(201).json({ schedule: fullSchedule });
    } catch (error) {
      next(error);
    }
  }

  async update(req, res, next) {
    try {
      const schedule = await Schedule.findByPk(req.params.id);
      if (!schedule) {
        return res.status(404).json({ error: 'Schedule not found' });
      }

      const { stopIds, ...scheduleData } = req.body;
      await schedule.update(scheduleData);

      if (stopIds && Array.isArray(stopIds)) {
        await ScheduleStop.destroy({ where: { schedule_id: schedule.id } });
        if (stopIds.length > 0) {
          await ScheduleStop.bulkCreate(
            stopIds.map((stopId) => ({ schedule_id: schedule.id, stop_id: stopId }))
          );
        }
      }

      const updated = await Schedule.findByPk(schedule.id, {
        include: [
          { model: Route },
          { model: Vehicle },
          { model: ScheduleStop, include: [{ model: Stop }] },
        ],
      });
      res.json({ schedule: updated });
    } catch (error) {
      next(error);
    }
  }

  async delete(req, res, next) {
    try {
      const schedule = await Schedule.findByPk(req.params.id);
      if (!schedule) return res.status(404).json({ error: 'Schedule not found' });

      if (schedule.image) {
        const imgPath = path.join(__dirname, '..', schedule.image);
        if (fs.existsSync(imgPath)) fs.unlinkSync(imgPath);
      }

      await schedule.destroy();
      res.json({ message: 'Schedule deleted' });
    } catch (error) {
      next(error);
    }
  }

  async uploadImage(req, res, next) {
    try {
      const schedule = await Schedule.findByPk(req.params.id);
      if (!schedule) return res.status(404).json({ error: 'Schedule not found' });

      if (!req.file) return res.status(400).json({ error: 'No file uploaded' });

      const imagePath = `/uploads/${req.file.filename}`;

      if (schedule.image) {
        const oldPath = path.join(__dirname, '..', schedule.image);
        if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath);
      }

      await schedule.update({ image: imagePath });

      res.json({ schedule: await Schedule.findByPk(schedule.id, {
        include: [
          { model: Route },
          { model: Vehicle },
          { model: ScheduleStop, include: [{ model: Stop }] },
        ],
      }) });
    } catch (error) {
      next(error);
    }
  }
}

module.exports = new ScheduleController();
