/**
 * vacationBalanceModel.js
 * 휴가 잔액 관련 데이터베이스 쿼리 모델
 */

const { getDb } = require('../dbPool');

const vacationBalanceModel = {
  /**
   * 특정 작업자의 모든 휴가 잔액 조회 (특정 연도)
   */
  async getByWorkerAndYear(userId, year) {
    const db = await getDb();
    const [rows] = await db.query(`
      SELECT
        svb.id, svb.user_id, svb.vacation_type_id, svb.year,
        svb.total_days, svb.used_days,
        (svb.total_days - svb.used_days) AS remaining_days,
        svb.balance_type, svb.expires_at, svb.notes,
        svb.created_by, svb.created_at, svb.updated_at,
        vt.type_name, vt.type_code, vt.priority, vt.is_special
      FROM sp_vacation_balances svb
      INNER JOIN vacation_types vt ON svb.vacation_type_id = vt.id
      WHERE svb.user_id = ? AND svb.year = ?
      ORDER BY vt.priority ASC, vt.type_name ASC
    `, [userId, year]);
    return rows;
  },

  /**
   * 특정 작업자의 특정 휴가 유형 잔액 조회
   */
  async getByWorkerTypeYear(userId, vacationTypeId, year) {
    const db = await getDb();
    const [rows] = await db.query(`
      SELECT
        svb.id, svb.user_id, svb.vacation_type_id, svb.year,
        svb.total_days, svb.used_days,
        (svb.total_days - svb.used_days) AS remaining_days,
        svb.balance_type, svb.expires_at,
        vt.type_name, vt.type_code
      FROM sp_vacation_balances svb
      INNER JOIN vacation_types vt ON svb.vacation_type_id = vt.id
      WHERE svb.user_id = ?
        AND svb.vacation_type_id = ?
        AND svb.year = ?
    `, [userId, vacationTypeId, year]);
    return rows;
  },

  /**
   * 모든 작업자의 휴가 잔액 조회 (특정 연도)
   */
  async getAllByYear(year) {
    const db = await getDb();
    const [rows] = await db.query(`
      SELECT
        svb.id, svb.user_id, svb.vacation_type_id, svb.year,
        svb.total_days, svb.used_days,
        (svb.total_days - svb.used_days) AS remaining_days,
        svb.balance_type, svb.expires_at, svb.notes,
        svb.created_by, svb.created_at, svb.updated_at,
        w.worker_name, w.employment_status,
        vt.type_name, vt.type_code, vt.priority
      FROM sp_vacation_balances svb
      INNER JOIN workers w ON svb.user_id = w.user_id
      INNER JOIN vacation_types vt ON svb.vacation_type_id = vt.id
      WHERE svb.year = ?
        AND w.employment_status = 'employed'
      ORDER BY w.worker_name ASC, vt.priority ASC
    `, [year]);
    return rows;
  },

  /**
   * 휴가 잔액 생성
   */
  async create(balanceData) {
    const db = await getDb();
    const [result] = await db.query(`INSERT INTO vacation_balance_details SET ?`, balanceData);
    return result;
  },

  /**
   * 휴가 잔액 수정
   */
  async update(id, updateData) {
    const db = await getDb();
    const [result] = await db.query(`UPDATE vacation_balance_details SET ? WHERE id = ?`, [updateData, id]);
    return result;
  },

  /**
   * 휴가 잔액 삭제
   */
  async delete(id) {
    const db = await getDb();
    const [result] = await db.query(`DELETE FROM vacation_balance_details WHERE id = ?`, [id]);
    return result;
  },

  /**
   * 작업자의 휴가 사용 일수 업데이트 (차감)
   */
  async deductDays(userId, vacationTypeId, year, daysToDeduct) {
    const db = await getDb();
    const [result] = await db.query(`
      UPDATE sp_vacation_balances
      SET used_days = used_days + ?,
          updated_at = NOW()
      WHERE user_id = ?
        AND vacation_type_id = ?
        AND year = ?
    `, [daysToDeduct, userId, vacationTypeId, year]);
    return result;
  },

  /**
   * 작업자의 휴가 사용 일수 복구 (취소)
   */
  async restoreDays(userId, vacationTypeId, year, daysToRestore) {
    const db = await getDb();
    const [result] = await db.query(`
      UPDATE sp_vacation_balances
      SET used_days = GREATEST(0, used_days - ?),
          updated_at = NOW()
      WHERE user_id = ?
        AND vacation_type_id = ?
        AND year = ?
    `, [daysToRestore, userId, vacationTypeId, year]);
    return result;
  },

  /**
   * 특정 작업자의 사용 가능한 휴가 일수 확인
   */
  async getAvailableVacationDays(userId, year) {
    const db = await getDb();
    const [rows] = await db.query(`
      SELECT
        svb.id,
        svb.vacation_type_id,
        vt.type_name,
        vt.type_code,
        vt.priority,
        svb.total_days,
        svb.used_days,
        (svb.total_days - svb.used_days) AS remaining_days,
        svb.balance_type
      FROM sp_vacation_balances svb
      INNER JOIN vacation_types vt ON svb.vacation_type_id = vt.id
      WHERE svb.user_id = ?
        AND svb.year = ?
        AND (svb.total_days - svb.used_days) > 0
      ORDER BY vt.priority ASC, FIELD(svb.balance_type, 'CARRY_OVER', 'AUTO', 'MANUAL', 'LONG_SERVICE', 'COMPANY_GRANT')
    `, [userId, year]);
    return rows;
  },

  /**
   * 작업자별 휴가 잔액 일괄 생성 (연도별)
   */
  async bulkCreate(balances) {
    if (!balances || balances.length === 0) {
      throw new Error('생성할 휴가 잔액 데이터가 없습니다');
    }

    const db = await getDb();
    const query = `INSERT INTO vacation_balance_details
      (user_id, vacation_type_id, year, total_days, used_days, notes, created_by)
      VALUES ?`;

    const values = balances.map(b => [
      b.user_id,
      b.vacation_type_id,
      b.year,
      b.total_days || 0,
      b.used_days || 0,
      b.notes || null,
      b.created_by
    ]);

    const [result] = await db.query(query, [values]);
    return result;
  },

  /**
   * 근속년수 기반 연차 일수 계산 (한국 근로기준법)
   */
  calculateAnnualLeaveDays(hireDate, targetYear) {
    const hire = new Date(hireDate);
    const targetDate = new Date(targetYear, 0, 1);

    const monthsDiff = (targetDate.getFullYear() - hire.getFullYear()) * 12
                     + (targetDate.getMonth() - hire.getMonth());

    if (monthsDiff < 12) {
      return Math.floor(monthsDiff);
    }

    const yearsWorked = Math.floor(monthsDiff / 12);
    const additionalDays = Math.floor((yearsWorked - 1) / 2);

    return Math.min(15 + additionalDays, 25);
  },

  /**
   * 휴가 사용 시 우선순위에 따라 잔액에서 차감
   */
  async deductByPriority(userId, year, daysToDeduct) {
    const db = await getDb();
    const conn = await db.getConnection();
    try {
      await conn.beginTransaction();

      const [balances] = await conn.query(`
        SELECT svb.id, svb.vacation_type_id, svb.total_days, svb.used_days,
               (svb.total_days - svb.used_days) AS remaining_days,
               svb.balance_type,
               vt.type_code, vt.type_name, vt.priority
        FROM sp_vacation_balances svb
        INNER JOIN vacation_types vt ON svb.vacation_type_id = vt.id
        WHERE svb.user_id = ? AND svb.year = ?
          AND (svb.total_days - svb.used_days) > 0
        ORDER BY vt.priority ASC, FIELD(svb.balance_type, 'CARRY_OVER', 'AUTO', 'MANUAL', 'LONG_SERVICE', 'COMPANY_GRANT')
        FOR UPDATE
      `, [userId, year]);

      if (balances.length === 0) {
        await conn.rollback();
        console.warn(`[VacationBalance] 작업자 ${userId}의 ${year}년 휴가 잔액이 없습니다`);
        return { success: false, message: '휴가 잔액이 없습니다', deducted: 0 };
      }

      let remaining = daysToDeduct;
      const deductions = [];

      for (const balance of balances) {
        if (remaining <= 0) break;
        const available = parseFloat(balance.remaining_days);
        const toDeduct = Math.min(remaining, available);
        if (toDeduct > 0) {
          await conn.query(`
            UPDATE sp_vacation_balances
            SET used_days = used_days + ?, updated_at = NOW()
            WHERE id = ?
          `, [toDeduct, balance.id]);
          deductions.push({ balance_id: balance.id, type_code: balance.type_code, type_name: balance.type_name, balance_type: balance.balance_type, deducted: toDeduct });
          remaining -= toDeduct;
        }
      }

      await conn.commit();
      console.log(`[VacationBalance] 작업자 ${userId}: ${daysToDeduct}일 차감 완료`, deductions);
      return { success: true, deductions, totalDeducted: daysToDeduct - remaining };
    } catch (err) {
      await conn.rollback();
      throw err;
    } finally {
      conn.release();
    }
  },

  /**
   * 휴가 취소 시 우선순위 역순으로 복구
   */
  async restoreByPriority(userId, year, daysToRestore) {
    const db = await getDb();
    const conn = await db.getConnection();
    try {
      await conn.beginTransaction();

      const [balances] = await conn.query(`
        SELECT svb.id, svb.vacation_type_id, svb.used_days,
               svb.balance_type,
               vt.type_code, vt.type_name, vt.priority
        FROM sp_vacation_balances svb
        INNER JOIN vacation_types vt ON svb.vacation_type_id = vt.id
        WHERE svb.user_id = ? AND svb.year = ?
          AND svb.used_days > 0
        ORDER BY vt.priority DESC, FIELD(svb.balance_type, 'COMPANY_GRANT', 'LONG_SERVICE', 'MANUAL', 'AUTO', 'CARRY_OVER')
        FOR UPDATE
      `, [userId, year]);

      let remaining = daysToRestore;
      const restorations = [];

      for (const balance of balances) {
        if (remaining <= 0) break;
        const usedDays = parseFloat(balance.used_days);
        const toRestore = Math.min(remaining, usedDays);
        if (toRestore > 0) {
          await conn.query(`
            UPDATE sp_vacation_balances
            SET used_days = used_days - ?, updated_at = NOW()
            WHERE id = ?
          `, [toRestore, balance.id]);
          restorations.push({ balance_id: balance.id, type_code: balance.type_code, type_name: balance.type_name, balance_type: balance.balance_type, restored: toRestore });
          remaining -= toRestore;
        }
      }

      await conn.commit();
      console.log(`[VacationBalance] 작업자 ${userId}: ${daysToRestore}일 복구 완료`, restorations);
      return { success: true, restorations, totalRestored: daysToRestore - remaining };
    } catch (err) {
      await conn.rollback();
      throw err;
    } finally {
      conn.release();
    }
  },

  /**
   * 특정 ID로 휴가 잔액 조회
   */
  async getById(id) {
    const db = await getDb();
    const [rows] = await db.query(`
      SELECT
        vbd.*,
        w.worker_name,
        vt.type_name,
        vt.type_code
      FROM vacation_balance_details vbd
      INNER JOIN workers w ON vbd.user_id = w.user_id
      INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
      WHERE vbd.id = ?
    `, [id]);
    return rows;
  }
};

module.exports = vacationBalanceModel;
