/**
 * Vacation Model
 *
 * vacation_types + sp_vacation_balances CRUD (MariaDB)
 * Sprint 001: workers → sso_users 기반 전환
 */

const { getPool } = require('./userModel');

/* ===== Vacation Types (휴가 유형) ===== */

async function getVacationTypes() {
  const db = getPool();
  const [rows] = await db.query(
    'SELECT * FROM vacation_types WHERE is_active = TRUE ORDER BY priority ASC, id ASC'
  );
  return rows;
}

async function getAllVacationTypes() {
  const db = getPool();
  const [rows] = await db.query(
    'SELECT * FROM vacation_types ORDER BY priority ASC, id ASC'
  );
  return rows;
}

async function getVacationTypeById(id) {
  const db = getPool();
  const [rows] = await db.query('SELECT * FROM vacation_types WHERE id = ?', [id]);
  return rows[0] || null;
}

async function createVacationType({ type_code, type_name, deduct_days, is_special, priority, description }) {
  const db = getPool();
  const [result] = await db.query(
    `INSERT INTO vacation_types (type_code, type_name, deduct_days, is_special, priority, description, is_system)
     VALUES (?, ?, ?, ?, ?, ?, FALSE)`,
    [type_code, type_name, deduct_days ?? 1.0, is_special ? 1 : 0, priority ?? 99, description || null]
  );
  return getVacationTypeById(result.insertId);
}

async function updateVacationType(id, data) {
  const db = getPool();
  const fields = [];
  const values = [];
  if (data.type_code !== undefined) { fields.push('type_code = ?'); values.push(data.type_code); }
  if (data.type_name !== undefined) { fields.push('type_name = ?'); values.push(data.type_name); }
  if (data.deduct_days !== undefined) { fields.push('deduct_days = ?'); values.push(data.deduct_days); }
  if (data.is_special !== undefined) { fields.push('is_special = ?'); values.push(data.is_special ? 1 : 0); }
  if (data.priority !== undefined) { fields.push('priority = ?'); values.push(data.priority); }
  if (data.description !== undefined) { fields.push('description = ?'); values.push(data.description || null); }
  if (data.is_active !== undefined) { fields.push('is_active = ?'); values.push(data.is_active ? 1 : 0); }
  if (fields.length === 0) return getVacationTypeById(id);
  values.push(id);
  await db.query(`UPDATE vacation_types SET ${fields.join(', ')} WHERE id = ?`, values);
  return getVacationTypeById(id);
}

async function deleteVacationType(id) {
  const db = getPool();
  // 시스템 유형은 비활성화만
  const type = await getVacationTypeById(id);
  if (type && type.is_system) {
    await db.query('UPDATE vacation_types SET is_active = FALSE WHERE id = ?', [id]);
  } else {
    await db.query('UPDATE vacation_types SET is_active = FALSE WHERE id = ?', [id]);
  }
}

async function updatePriorities(items) {
  const db = getPool();
  for (const { id, priority } of items) {
    await db.query('UPDATE vacation_types SET priority = ? WHERE id = ?', [priority, id]);
  }
}

/* ===== Vacation Balances (연차 배정) — sp_vacation_balances + sso_users ===== */

async function getBalancesByYear(year) {
  const db = getPool();
  const [rows] = await db.query(
    `SELECT vb.*, vt.type_code, vt.type_name, vt.deduct_days, vt.priority,
            su.name AS user_name, su.hire_date, su.department_id, su.long_service_excluded,
            COALESCE(d.department_name, '미배정') AS department_name
     FROM sp_vacation_balances vb
     JOIN vacation_types vt ON vb.vacation_type_id = vt.id
     JOIN sso_users su ON vb.user_id = su.user_id
     LEFT JOIN departments d ON su.department_id = d.department_id
     WHERE su.is_active = 1
       AND (
         vb.year = ?
         OR (vb.balance_type = 'LONG_SERVICE'
             AND (vb.expires_at IS NULL OR vb.expires_at >= CURDATE()))
       )
     ORDER BY d.department_name ASC, su.name ASC, vt.priority ASC`,
    [year]
  );
  return rows;
}

async function getBalancesByUserYear(userId, year) {
  const db = getPool();
  const [rows] = await db.query(
    `SELECT vb.*, vt.type_code, vt.type_name, vt.deduct_days, vt.priority
     FROM sp_vacation_balances vb
     JOIN vacation_types vt ON vb.vacation_type_id = vt.id
     WHERE vb.user_id = ? AND (
       vb.year = ?
       OR (vb.balance_type = 'LONG_SERVICE'
           AND (vb.expires_at IS NULL OR vb.expires_at >= CURDATE()))
     )
     ORDER BY vt.priority ASC`,
    [userId, year]
  );
  return rows;
}

async function getBalanceById(id) {
  const db = getPool();
  const [rows] = await db.query(
    `SELECT vb.*, vt.type_code, vt.type_name
     FROM sp_vacation_balances vb
     JOIN vacation_types vt ON vb.vacation_type_id = vt.id
     WHERE vb.id = ?`,
    [id]
  );
  return rows[0] || null;
}

async function createBalance({ user_id, vacation_type_id, year, total_days, used_days, notes, created_by, balance_type, expires_at }) {
  const db = getPool();
  const [result] = await db.query(
    `INSERT INTO sp_vacation_balances (user_id, vacation_type_id, year, total_days, used_days, notes, created_by, balance_type, expires_at)
     VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
     ON DUPLICATE KEY UPDATE total_days = VALUES(total_days), used_days = VALUES(used_days), notes = VALUES(notes)`,
    [user_id, vacation_type_id, year, total_days ?? 0, used_days ?? 0, notes || null, created_by, balance_type || 'AUTO', expires_at || null]
  );
  return result.insertId ? getBalanceById(result.insertId) : null;
}

async function updateBalance(id, data) {
  const db = getPool();
  const fields = [];
  const values = [];
  if (data.total_days !== undefined) { fields.push('total_days = ?'); values.push(data.total_days); }
  if (data.used_days !== undefined) { fields.push('used_days = ?'); values.push(data.used_days); }
  if (data.notes !== undefined) { fields.push('notes = ?'); values.push(data.notes || null); }
  if (fields.length === 0) return getBalanceById(id);
  values.push(id);
  await db.query(`UPDATE sp_vacation_balances SET ${fields.join(', ')} WHERE id = ?`, values);
  return getBalanceById(id);
}

async function deleteBalance(id) {
  const db = getPool();
  await db.query('DELETE FROM sp_vacation_balances WHERE id = ?', [id]);
}

async function bulkUpsertBalances(balances) {
  const db = getPool();
  let count = 0;
  for (const b of balances) {
    await db.query(
      `INSERT INTO sp_vacation_balances (user_id, vacation_type_id, year, total_days, used_days, notes, created_by, balance_type, expires_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
       ON DUPLICATE KEY UPDATE total_days = VALUES(total_days), notes = VALUES(notes)`,
      [b.user_id, b.vacation_type_id, b.year, b.total_days ?? 0, b.used_days ?? 0, b.notes || null, b.created_by, b.balance_type || 'AUTO', b.expires_at || null]
    );
    count++;
  }
  return count;
}

/* ===== 연차 자동 계산 (근로기준법 + vacation_settings) ===== */

function calculateAnnualDays(hireDate, targetYear, settings = {}) {
  if (!hireDate) return 0;
  const hire = new Date(hireDate);
  const yearStart = new Date(targetYear, 0, 1);
  const monthsDiff = (yearStart.getFullYear() - hire.getFullYear()) * 12 + (yearStart.getMonth() - hire.getMonth());

  if (monthsDiff < 0) return 0;

  const firstYearRule = settings.annual_leave_first_year_rule || 'MONTHLY';
  const baseDays = parseInt(settings.annual_leave_base_days) || 15;
  const incrementPerYears = parseInt(settings.annual_leave_increment_per_years) || 2;
  const maxDays = parseInt(settings.annual_leave_max_days) || 25;

  if (monthsDiff < 12) {
    // 1년 미만
    if (firstYearRule === 'NONE') return 0;
    return Math.max(0, Math.floor(monthsDiff));
  }
  // 1년 이상: baseDays + incrementPerYears마다 1일 추가 (최대 maxDays)
  const yearsWorked = Math.floor(monthsDiff / 12);
  const additional = incrementPerYears > 0 ? Math.floor((yearsWorked - 1) / incrementPerYears) : 0;
  return Math.min(baseDays + additional, maxDays);
}

async function autoCalculateForAllUsers(year, createdBy, departmentId) {
  const db = getPool();

  // vacation_settings 로드
  const vacationSettingsModel = require('./vacationSettingsModel');
  const settings = await vacationSettingsModel.loadAsObject();

  // sso_users 조회 (부서 필터 선택)
  let userQuery = 'SELECT user_id, name, hire_date FROM sso_users WHERE is_active = 1 AND partner_company_id IS NULL';
  const params = [];
  if (departmentId) {
    userQuery += ' AND department_id = ?';
    params.push(departmentId);
  }
  userQuery += ' ORDER BY name';
  const [users] = await db.query(userQuery, params);

  // 연차 유형 (ANNUAL_FULL) 찾기
  const [types] = await db.query(
    "SELECT id FROM vacation_types WHERE type_code = 'ANNUAL_FULL' AND is_active = TRUE LIMIT 1"
  );
  if (!types.length) return { count: 0, results: [] };
  const annualTypeId = types[0].id;

  const results = [];
  for (const u of users) {
    const days = calculateAnnualDays(u.hire_date, year, settings);
    if (days > 0) {
      await db.query(
        `INSERT INTO sp_vacation_balances (user_id, vacation_type_id, year, total_days, used_days, notes, created_by, balance_type)
         VALUES (?, ?, ?, ?, 0, ?, ?, 'AUTO')
         ON DUPLICATE KEY UPDATE total_days = VALUES(total_days), notes = VALUES(notes)`,
        [u.user_id, annualTypeId, year, days, `자동계산 (입사: ${u.hire_date ? new Date(u.hire_date).toISOString().substring(0, 10) : ''})`, createdBy]
      );
      results.push({ user_id: u.user_id, user_name: u.name, days, hire_date: u.hire_date });
    }
  }

  // 장기근속 자동 부여
  if (settings.long_service_auto_grant === 'true') {
    const longServiceResults = await autoGrantLongServiceLeave(users, year, createdBy, settings);
    results.push(...longServiceResults);
  }

  return { count: results.length, results };
}

async function autoGrantLongServiceLeave(users, year, createdBy, settings) {
  const db = getPool();

  const thresholdYears = parseInt(settings.long_service_threshold_years) || 5;
  const bonusDays = parseInt(settings.long_service_bonus_days) || 3;

  // 연차 유형 (ANNUAL_FULL) 찾기
  const [types] = await db.query(
    "SELECT id FROM vacation_types WHERE type_code = 'ANNUAL_FULL' AND is_active = TRUE LIMIT 1"
  );
  if (!types.length) return [];
  const annualTypeId = types[0].id;

  const results = [];
  for (const u of users) {
    if (!u.hire_date) continue;

    const hire = new Date(u.hire_date);
    const yearsWorked = year - hire.getFullYear();
    // 해당 연도 내 threshold 도래 건만 (소급 없음)
    if (yearsWorked !== thresholdYears) continue;

    // 기념일이 해당 연도인지 확인
    const anniversaryDate = new Date(hire);
    anniversaryDate.setFullYear(hire.getFullYear() + thresholdYears);
    if (anniversaryDate.getFullYear() !== year) continue;

    // long_service_excluded 체크
    const [userRows] = await db.query(
      'SELECT long_service_excluded FROM sso_users WHERE user_id = ?',
      [u.user_id]
    );
    if (userRows[0] && userRows[0].long_service_excluded) continue;

    // 이미 LONG_SERVICE 부여 여부 체크
    const [existing] = await db.query(
      `SELECT id FROM sp_vacation_balances
       WHERE user_id = ? AND balance_type = 'LONG_SERVICE' LIMIT 1`,
      [u.user_id]
    );
    if (existing.length > 0) continue;

    await db.query(
      `INSERT INTO sp_vacation_balances (user_id, vacation_type_id, year, total_days, used_days, notes, created_by, balance_type, expires_at)
       VALUES (?, ?, ?, ?, 0, ?, ?, 'LONG_SERVICE', NULL)
       ON DUPLICATE KEY UPDATE total_days = VALUES(total_days), notes = VALUES(notes)`,
      [u.user_id, annualTypeId, year, bonusDays, `장기근속 ${thresholdYears}년 자동부여`, createdBy]
    );
    results.push({ user_id: u.user_id, user_name: u.name, days: bonusDays, type: 'LONG_SERVICE' });
  }

  return results;
}

module.exports = {
  getVacationTypes, getAllVacationTypes, getVacationTypeById,
  createVacationType, updateVacationType, deleteVacationType, updatePriorities,
  getBalancesByYear, getBalancesByUserYear, getBalanceById,
  createBalance, updateBalance, deleteBalance, bulkUpsertBalances,
  calculateAnnualDays, autoCalculateForAllUsers, autoGrantLongServiceLeave
};
