const express = require('express');
const router = express.Router();
const passport = require('passport');
const db = require('../db/models');
const { wrapAsync } = require('../helpers');
const { Op } = require('sequelize');

const getInclusiveDateRange = (startDate, endDate) => ({
  start: new Date(`${startDate}T00:00:00.000Z`),
  end: new Date(`${endDate}T23:59:59.999Z`),
});

router.post('/preview', passport.authenticate('jwt', { session: false }), wrapAsync(async (req, res) => {
  const { startDate, endDate } = req.body;
  
  if (!startDate || !endDate) {
    return res.status(400).send('startDate and endDate are required');
  }

  const { start, end } = getInclusiveDateRange(startDate, endDate);

  // Find job logs in range that are not paid
  const jobLogs = await db.job_logs.findAll({
    where: {
      work_date: {
        [Op.between]: [start, end]
      },
      status: {
        [Op.ne]: 'paid'
      }
    },
    include: [
      { model: db.users, as: 'employee' },
      { model: db.pay_types, as: 'pay_type' }
    ]
  });

  const employeeTotals = {};

  jobLogs.forEach(log => {
    const empId = log.employeeId;
    if (!empId) return;

    if (!employeeTotals[empId]) {
      employeeTotals[empId] = {
        employee: log.employee,
        total_hours: 0,
        total_commission_base: 0,
        gross_pay: 0,
        job_log_ids: []
      };
    }

    const totals = employeeTotals[empId];
    totals.job_log_ids.push(log.id);

    const hours = parseFloat(log.hours_conducted || 0);
    totals.total_hours += hours;

    const clientPaid = parseFloat(log.client_paid || 0);
    totals.total_commission_base += clientPaid;

    let pay = 0;
    if (log.pay_type) {
      if (log.pay_type.pay_method === 'hourly') {
        pay = hours * parseFloat(log.pay_type.hourly_rate || 0);
      } else if (log.pay_type.pay_method === 'commission') {
        pay = clientPaid * (parseFloat(log.pay_type.commission_rate || 0) / 100);
      }
    }
    totals.gross_pay += pay;
  });

  const summary = {
    totalGrossPay: 0,
    totalHours: 0
  };

  const lineItems = Object.values(employeeTotals).map(item => {
    summary.totalGrossPay += item.gross_pay;
    summary.totalHours += item.total_hours;
    return item;
  });

  res.json({ lineItems, summary });
}));

router.post('/generate', passport.authenticate('jwt', { session: false }), wrapAsync(async (req, res) => {
  const { startDate, endDate, name, notes } = req.body;
  
  if (!startDate || !endDate) {
    return res.status(400).send('startDate and endDate are required');
  }

  const { start, end } = getInclusiveDateRange(startDate, endDate);

  // Find job logs
  const jobLogs = await db.job_logs.findAll({
    where: {
      work_date: {
        [Op.between]: [start, end]
      },
      status: {
        [Op.ne]: 'paid'
      }
    },
    include: [
      { model: db.users, as: 'employee' },
      { model: db.pay_types, as: 'pay_type' }
    ]
  });

  if (jobLogs.length === 0) {
    return res.status(400).send('No unpaid job logs found in this date range.');
  }

  const employeeTotals = {};

  jobLogs.forEach(log => {
    const empId = log.employeeId;
    if (!empId) return;

    if (!employeeTotals[empId]) {
      employeeTotals[empId] = {
        total_hours: 0,
        total_commission_base: 0,
        gross_pay: 0,
        workers_comp_amount: 0,
        employeeId: empId,
        job_log_ids: []
      };
    }

    const totals = employeeTotals[empId];
    totals.job_log_ids.push(log.id);

    const hours = parseFloat(log.hours_conducted || 0);
    totals.total_hours += hours;

    const clientPaid = parseFloat(log.client_paid || 0);
    totals.total_commission_base += clientPaid;

    let pay = 0;
    let wcAmount = 0;
    if (log.pay_type) {
      if (log.pay_type.pay_method === 'hourly') {
        pay = hours * parseFloat(log.pay_type.hourly_rate || 0);
      } else if (log.pay_type.pay_method === 'commission') {
        pay = clientPaid * (parseFloat(log.pay_type.commission_rate || 0) / 100);
      }
      if (log.pay_type.workers_comp_percentage) {
        wcAmount = pay * (parseFloat(log.pay_type.workers_comp_percentage) / 100);
      }
    }
    totals.gross_pay += pay;
    totals.workers_comp_amount += wcAmount;
  });

  // Create a transaction
  const transaction = await db.sequelize.transaction();
  try {
    // Create payroll run
    const payrollRun = await db.payroll_runs.create({
      name: name || `Payroll ${startDate} to ${endDate}`,
      period_start: startDate,
      period_end: endDate,
      run_date: new Date(),
      status: 'finalized',
      notes: notes || '',
      createdById: req.currentUser.id
    }, { transaction });

    // Create line items
    for (const empId of Object.keys(employeeTotals)) {
      const totals = employeeTotals[empId];
      await db.payroll_line_items.create({
        payroll_runId: payrollRun.id,
        employeeId: totals.employeeId,
        total_hours: totals.total_hours,
        gross_pay: totals.gross_pay,
        total_commission_base: totals.total_commission_base,
        workers_comp_amount: totals.workers_comp_amount,
        createdById: req.currentUser.id
      }, { transaction });

      // Mark job logs as paid
      await db.job_logs.update({
        status: 'paid'
      }, {
        where: {
          id: {
            [Op.in]: totals.job_log_ids
          }
        },
        transaction
      });
    }

    await transaction.commit();
    res.json({ success: true, payrollRunId: payrollRun.id });
  } catch (error) {
    await transaction.rollback();
    console.error(error);
    res.status(500).send('Error generating payroll');
  }
}));

module.exports = router;
