const response = require('../../../helpers/responses');
const db = require('../../../../models/migration');
const errorHandler = require('../../../middlewares/errorHandler')
const { sequelize, Op } = require('../../../../models/migration');
const { getDistance } = require('../../../helpers/distance');
const moment = require('moment-timezone')
const path = require("path");
const fs = require("fs");
const axios = require("axios");
const User = db.User
const Attedances = db.Attedances
const Branch = db.Branch

const saveUploadedFile = (file, folder = "public/uploads") => {
  const dir = path.join(process.cwd(), folder);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

  const fileName = `${Date.now()}-${file.originalname}`;
  const filePath = path.join(dir, fileName);

  // file.buffer karena kita pakai memoryStorage
  fs.writeFileSync(filePath, file.buffer);

  // return URL yang bisa diakses dari frontend
  return `/${folder.replace("public/", "")}/${fileName}`.replace(/\\/g, "/");
};

const create = async (req, res) => {
  const t = await sequelize.transaction();
  try {
    const { type, lat, lng, reason } = req.body;
    const user_id = req.user.id;
    const now = moment().tz('Asia/Jakarta');
    const today = now.format('YYYY-MM-DD');
    const currentHour = parseInt(now.format('HH'));


    const user = await User.findOne({ where: { id: user_id } });
    if (!user) {
      await t.rollback()
      return response.failed(res, 404, 'User tidak ditemukan');
    }

    const branch = await Branch.findOne({ where: { id: user.branch_id } });
    if (!branch) {
      await t.rollback()
      return response.failed(res, 404, 'Branch kantor tidak ditemukan');
    }

    let attendance = await Attedances.findOne({
      where: { user_id, date: today },
    });

    if (attendance && ['sick', 'permission'].includes(attendance.type)) {
      await t.rollback()
      return response.failed(res, 400, `Hari ini Anda sudah absen ${attendance.type}`);
    }
    
    // === Jika izin (sakit / izin) ===
    if (['sick', 'permission'].includes(type)) {
      if (attendance) return response.failed(res, 400, 'Sudah ada absensi hari ini');

      attendance = await Attedances.create({
        user_id,
        branch_id: user.branch_id,
        name: user.name,
        type,
        reason,
        date: today,
      }, { transaction: t });

      await t.commit();
      return response.success(res, attendance, 'Izin berhasil disimpan');
    }

    if (attendance && attendance.clock_in) {
      // 🕛 Jika branch tidak mengaktifkan absen siang
      if (!branch.lunch_attendance) {
        // Langsung lewati logika absen siang dan lanjut ke clock_out
        if (currentHour >= 15 && !attendance.clock_out) {
          attendance.clock_out = now.toDate();
    
          const durationMs = moment(attendance.clock_out).diff(moment(attendance.clock_in));
          const hours = Math.floor(durationMs / (1000 * 60 * 60));
          const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
          attendance.work_duration = `${hours} jam ${minutes} menit`;
    
          await attendance.save({ transaction: t });
          await t.commit();
    
          return response.success(
            res,
            {
              ...attendance.toJSON(),
              branch_name: branch.name,
              clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
              clock_out: moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
              work_duration: attendance.work_duration,
            },
            'Absen pulang berhasil'
          );
        }
    
        if (currentHour < 15 && !attendance.clock_out) {
          await t.rollback();
          return response.failed(res, 400, 'Belum waktunya absen pulang');
        }
      } else {
        // 🕛 Jika branch pakai absen siang
        if (currentHour >= 12 && currentHour < 15 && !attendance.lunch_in) {
          attendance.lunch_in = now.toDate();
          await attendance.save({ transaction: t });
          await t.commit();
          return response.success(res, attendance, 'Absen masuk setelah makan siang berhasil');
        }
    
        if (currentHour < 12 && !attendance.lunch_in) {
          await t.rollback();
          return response.failed(res, 400, 'Belum waktunya absen setelah makan siang');
        }
    
        if (currentHour >= 15 && !attendance.clock_out) {
          attendance.clock_out = now.toDate();
    
          const durationMs = moment(attendance.clock_out).diff(moment(attendance.clock_in));
          const hours = Math.floor(durationMs / (1000 * 60 * 60));
          const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
          attendance.work_duration = `${hours} jam ${minutes} menit`;
    
          await attendance.save({ transaction: t });
          await t.commit();
    
          return response.success(
            res,
            {
              ...attendance.toJSON(),
              branch_name: branch.name,
              clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
              clock_out: moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
              work_duration: attendance.work_duration,
            },
            'Absen pulang berhasil'
          );
        }
    
        if (currentHour < 15 && attendance.lunch_in && !attendance.clock_out) {
          await t.rollback();
          return response.failed(res, 400, 'Belum waktunya absen pulang');
        }
      }
    
      await t.rollback();
      return response.failed(res, 400, 'Sudah melakukan absen hari ini');
    }
    
    // === Jika sudah absen masuk tapi belum pulang ===
    // if (attendance && attendance.clock_in && !attendance.clock_out) {
    //   attendance.clock_out = now.toDate(); // waktu WIB
    //   const durationMs = moment(attendance.clock_out).diff(moment(attendance.clock_in));
    //   const hours = Math.floor(durationMs / (1000 * 60 * 60));
    //   const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
    //   attendance.work_duration = `${hours} jam ${minutes} menit`;

    //   await attendance.save({ transaction: t });
    //   await t.commit();
    //   return response.success(res, {
    //     ...attendance.toJSON(),
    //     clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
    //     clock_out: attendance.clock_out
    //       ? moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
    //       : null
    //   }, 'Absen pulang berhasil');
    // }

    // === Jika belum absen sama sekali, cek lokasi ===
    const distance = getDistance(branch.lat, branch.lng, lat, lng);
    const allowedRadius = parseFloat(process.env.ABSENCE_RADIUS) || 100;

    if (distance > allowedRadius) {
      await t.rollback();
      return response.failed(res, 400, `Lokasi di luar area kantor (${distance.toFixed(2)} meter)`);
    }

    let finalPhotoUrl = null;

    if (req.file) {
      // Jika upload file langsung
      finalPhotoUrl = saveUploadedFile(req.file, "public/uploads/attendance_photos");
    } else if (req.body.photo) {
      // Jika kirim URL dari FE (misal kamera web base64 atau URL publik)
      const imageUrl = req.body.photo;
      const fileName = `${Date.now()}-${Math.random()
        .toString(36)
        .substring(7)}.jpg`;
      const filePath = path.join("public/uploads/attendance_photos", fileName);

      const responseImg = await axios({
        url: imageUrl,
        responseType: "arraybuffer",
      });

      fs.writeFileSync(filePath, responseImg.data);
      finalPhotoUrl = filePath.replace("public", "").replace(/\\/g, "/");
    }

    // === Absen masuk ===
    attendance = await Attedances.create({
      user_id,
      name: user.name,
      photo: finalPhotoUrl,
      branch_id: user.branch_id,
      type: 'present',
      date: today,
      clock_in: now.toDate(),
      lat,
      lng,
    }, { transaction: t });

    await t.commit();
    return response.success(res, {
      ...attendance.toJSON(),
      branch_name: branch.name,
      clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
      clock_out: attendance.clock_out
        ? moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
        : null
    }, 'Absen masuk berhasil');
  } catch (error) {
    await t.rollback();
    errorHandler(error, res, req);
    return response.failed(res, 500, error.message);
  }
};

const clockOut = async (req, res) => {
  const t = await sequelize.transaction();
  try {
    const user_id = req.user.id;
    const now = moment().tz('Asia/Jakarta');
    const today = now.format('YYYY-MM-DD');

    const user = await User.findOne({ where: { id: user_id } });
    if (!user) return response.failed(res, 404, 'User tidak ditemukan');

    const attendance = await Attedances.findOne({
      where: { user_id, date: today, type: 'present' },
    });

    if (!attendance) {
      await t.rollback();
      return response.failed(res, 400, 'Belum absen masuk hari ini');
    }

    if (attendance.clock_out) {
      await t.rollback();
      return response.failed(res, 400, 'Sudah absen pulang hari ini');
    }
    const branch = await Branch.findOne({ where: { id: user.branch_id } });

    // Set jam pulang (tanpa cek lokasi)
    attendance.clock_out = now.toDate();

    // Hitung durasi kerja
    const durationMs = moment(attendance.clock_out).diff(moment(attendance.clock_in));
    const hours = Math.floor(durationMs / (1000 * 60 * 60));
    const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
    attendance.work_duration = `${hours} jam ${minutes} menit`;

    await attendance.save({ transaction: t });
    await t.commit();

    return response.success(res, {
      ...attendance.toJSON(),
      branch_name: branch.name,
      clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
      clock_out: moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
      work_duration: attendance.work_duration,
    }, 'Absen pulang berhasil');
  } catch (error) {
    await t.rollback();
    errorHandler(error, res, req);
    return response.failed(res, 500, error.message);
  }
};



const history = async (req, res) => {
  try {
    const user_id = req.user?.id;
    if (!user_id) return response.failed(res, 401, "User tidak terautentikasi");

    const { type } = req.query;
    const where = { user_id };

    if (type === 'today') {
      const today = new Date(Date.now() + 7 * 60 * 60 * 1000).toISOString().split('T')[0];
      where.date = today; // pakai equality, bukan LIKE
    }

    const attendances = await Attedances.findAll({
      where,
      order: [['date', 'DESC']],
    });

    if (!attendances.length)
      return response.success(res, [], "Tidak ada data absensi");

    const result = attendances.map(a => {
      let duration = null;

      if (a.clock_in && a.clock_out && a.lunch_in) {
        const diffMs = new Date(a.clock_out) - new Date(a.clock_in);
        const totalMinutes = Math.floor(diffMs / 60000);
        const hours = Math.floor(totalMinutes / 60);
        const minutes = totalMinutes % 60;
        duration = `${hours} jam ${minutes} menit`;
      }

      return {
        name: a.name,
        date: moment(a.date).tz('Asia/Jakarta').format('YYYY-MM-DD'),
        clock_in: a.clock_in
          ? moment(a.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
          : '-',
        clock_out: a.clock_out
          ? moment(a.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
          : '-',
        lunch_in: a.lunch_in
          ? moment(a.lunch_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
          : '-',
        duration: duration || '-',
        type: a.type,
      };
    });

    return response.success(res, result, "Riwayat absensi berhasil dimuat");
  } catch (error) {
    errorHandler(error, req, res);
    return response.failed(res, 500, error.message);
  }
};

const getAll = async (req, res) => {
  try {

    const today = moment().tz('Asia/Jakarta').format('YYYY-MM-DD');

    const attendances = await Attedances.findAll({
      where: {
        date: today
      },
      include: [
        {
          model: User,
          as: 'user',
          attributes: ['id', 'name', 'email']
        }
      ],
      order: [['created_at', 'DESC']]
    });

    return response.success(res, attendances, 'List kehadiran hari ini');
  } catch (error) {
    console.error(error);
    return response.error(res, 'Gagal mengambil data absensi hari ini');
  }
};

const update = async (req, res) => {
  const t = await sequelize.transaction();
  try {
    const id = req.params.id;
    const user_id = req.user.id;
    const body = req.body;

    const attedances = await Attedances.findOne({
      where: { id },
      transaction: t
    });

    const attedancesUpdate = await attedances.update({
      ...body,
      user_id,
    }, { transaction: t });

    await t.commit();
    return response.success(res, attedancesUpdate, 'Category Berhasil Di update');
  } catch (error) {
    await t.rollback();
    errorHandler(error, req, res);
    return response.failed(res, 500, error.message);
  }
}

const destroy = async (req, res) => {
  const t = await sequelize.transaction();
  try {
    const id = req.params.id;
    const attendance = await Attedances.findOne({
      where: { id },
      transaction: t,
    });

    if (!attendance) {
      await t.rollback();
      return response.failed(res, 404, 'Attendance tidak ditemukan');
    }

    await attendance.destroy({ transaction: t });
    await t.commit();

    return response.success(res, null, 'Attendance berhasil dihapus');
  } catch (error) {
    await t.rollback();
    errorHandler(error, req, res);
    return response.failed(res, 500, error.message);
  }
};

const checkLocation = async (req, res) => {
  try {
    const user_id = req.user.id;
    const { lat, lng } = req.query;

    if (!lat || !lng) {
      return response.failed(res, 400, 'Latitude dan longitude wajib dikirim');
    }

    const user = await User.findOne({ where: { id: user_id } });
    if (!user) return response.failed(res, 404, 'User tidak ditemukan');

    const branch = await Branch.findOne({ where: { id: user.branch_id } });
    if (!branch) return response.failed(res, 404, 'Data kantor tidak ditemukan');

    // Hitung jarak user dengan kantor (dalam meter)
    const distanceMeters = getDistance(branch.lat, branch.lng, lat, lng);

    // Otomatis ubah ke km jika lebih dari 1000 meter
    const isKm = distanceMeters >= 1000;
    const distance = isKm ? distanceMeters / 1000 : distanceMeters;
    const unit = isKm ? 'km' : 'm';

    // Radius tetap pakai meter (biar konsisten)
    const allowedRadius = parseFloat(process.env.ABSENCE_RADIUS) || 100;

    if (distanceMeters <= allowedRadius) {
      return response.success(res, {
        inOffice: true,
        branch_name: branch.name,
        distance: distance.toFixed(2),
        unit,
        message: `Anda sedang berada di lokasi ${branch.name}, silakan absen.`,
      });
    }

    return response.success(res, {
      inOffice: false,
      branch_name: branch.name,
      distance: distance.toFixed(2),
      unit,
      message: `Anda berada di luar area kantor ${distance.toFixed(2)} ${unit} dari ${branch.name}.`,
    });
  } catch (error) {
    errorHandler(error, req, res);
    return response.failed(res, 500, error.message);
  }
};

module.exports = {
  create,
  destroy,
  history,
  update,
  clockOut,
  getAll,
  checkLocation
}