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

class AttendanceModel {
  // 일일 근태 기록 조회
  static async getDailyAttendanceRecords(date, userId = null) {
    const db = await getDb();
    let query = `
      SELECT
        dar.*,
        w.worker_name,
        w.job_type,
        wat.type_name as attendance_type_name,
        wat.type_code as attendance_type_code,
        vt.type_name as vacation_type_name,
        vt.type_code as vacation_type_code,
        vt.deduct_days as vacation_days
      FROM daily_attendance_records dar
      LEFT JOIN workers w ON dar.user_id = w.user_id
      LEFT JOIN work_attendance_types wat ON dar.attendance_type_id = wat.id
      LEFT JOIN vacation_types vt ON dar.vacation_type_id = vt.id
      WHERE dar.record_date = ?
    `;

    const params = [date];

    if (userId) {
      query += ' AND dar.user_id = ?';
      params.push(userId);
    }

    query += ' ORDER BY w.worker_name';

    const [rows] = await db.execute(query, params);
    return rows;
  }

  // 기간별 근태 기록 조회 (월별 조회용)
  static async getDailyRecords(startDate, endDate, userId = null) {
    const db = await getDb();
    let query = `
      SELECT
        dar.*,
        w.worker_name,
        w.job_type,
        wat.type_name as attendance_type_name,
        wat.type_code as attendance_type_code,
        vt.type_name as vacation_type_name,
        vt.type_code as vacation_type_code,
        vt.deduct_days as vacation_days
      FROM daily_attendance_records dar
      LEFT JOIN workers w ON dar.user_id = w.user_id
      LEFT JOIN work_attendance_types wat ON dar.attendance_type_id = wat.id
      LEFT JOIN vacation_types vt ON dar.vacation_type_id = vt.id
      WHERE dar.record_date BETWEEN ? AND ?
    `;

    const params = [startDate, endDate];

    if (userId) {
      query += ' AND dar.user_id = ?';
      params.push(userId);
    }

    query += ' ORDER BY dar.record_date ASC';

    const [rows] = await db.execute(query, params);
    return rows;
  }

  // 작업 보고서와 근태 기록 동기화 (시간 합산 및 상태 업데이트)
  static async syncWithWorkReports(userId, date) {
    const db = await getDb();

    // 1. 해당 날짜의 총 작업 시간 계산
    const [reportStats] = await db.execute(`
      SELECT
        COALESCE(SUM(work_hours), 0) as total_hours,
        COUNT(*) as report_count
      FROM daily_work_reports
      WHERE user_id = ? AND report_date = ?
    `, [userId, date]);

    const totalHours = parseFloat(reportStats[0].total_hours || 0);
    const reportCount = reportStats[0].report_count;

    // 2. 근태 유형 및 상태 결정
    // 기본 규칙: 0시간 -> incomplete, <8시간 -> partial, 8시간 -> complete, >8시간 -> overtime
    // (휴가는 별도 로직이지만 여기서 덮어쓰지 않도록 주의해야 함. 하지만 작업보고서가 추가되면 실 근무로 간주)

    let status = 'incomplete';
    let typeCode = 'REGULAR'; // 기본값

    if (totalHours === 0) {
      status = 'incomplete';
    } else if (totalHours < 8) {
      status = 'partial';
      typeCode = 'PARTIAL';
    } else if (totalHours === 8) {
      status = 'complete';
      typeCode = 'REGULAR';
    } else {
      status = 'overtime';
      typeCode = 'OVERTIME';
    }

    // 근태 유형 ID 조회
    const [types] = await db.execute('SELECT id FROM work_attendance_types WHERE type_code = ?', [typeCode]);
    const typeId = types[0]?.id;

    // 3. 기록 업데이트 (휴가 정보는 유지)
    // 기존 기록 조회
    const [existing] = await db.execute(
      'SELECT id, vacation_type_id FROM daily_attendance_records WHERE user_id = ? AND record_date = ?',
      [userId, date]
    );

    if (existing.length > 0) {
      // 휴가가 설정되어 있고 시간이 0이면 휴가 상태 유지, 시간이 있으면 근무+휴가 복합 상태일 수 있음
      // 여기서는 단순화하여 근무 시간이 있으면 근무 상태로 업데이트 (단, vacation_type_id는 유지)

      const recordId = existing[0].id;
      // 만약 기존 상태가 'vacation'이고 근무시간이 0이면 업데이트 건너뛸 수도 있지만,
      // 작업보고서가 삭제되어 0이 된 경우도 있으므로 업데이트는 수행해야 함.

      await db.execute(`
        UPDATE daily_attendance_records
        SET 
          total_work_hours = ?,
          attendance_type_id = ?,
          status = ?,
          updated_at = CURRENT_TIMESTAMP
        WHERE id = ?
      `, [totalHours, typeId, status, recordId]);

      return { synced: true, totalHours, status };
    } else {
      // 기록이 없으면 생성 (일반적으로는 initializeDailyRecords로 생성되어 있어야 함)
      // 생성자가 명확하지 않으므로 시스템(1) 또는 알 수 없음 처리
      await db.execute(`
        INSERT INTO daily_attendance_records
        (record_date, user_id, total_work_hours, attendance_type_id, status, created_by)
        VALUES (?, ?, ?, ?, ?, 1)
      `, [date, userId, totalHours, typeId, status]);

      return { synced: true, totalHours, status, created: true };
    }
  }

  // 일일 근태 기록 초기화 (모든 활성 작업자에 대한 기본 레코드 생성)
  static async initializeDailyRecords(date, createdBy) {
    const db = await getDb();

    // 1. 활성 작업자 조회
    const [workers] = await db.execute(
      'SELECT user_id FROM workers WHERE status = "active" AND user_id IS NOT NULL'
    );

    if (workers.length === 0) return { inserted: 0 };

    // 2. 일일 근태 레코드 일괄 생성 (이미 존재하면 무시)
    // VALUES (...), (...), ...
    const values = workers.map(w => [date, w.user_id, 'incomplete', createdBy]);

    // Bulk INSERT IGNORE
    // Note: mysql2 execute doesn't support nested arrays for bulk insert easily with placeholder ?
    // We should build the query or use query method for pool?
    // Using simple loop for safety and compatibility or building string.

    let insertedCount = 0;

    // 트랜잭션 사용 권장
    const conn = await db.getConnection();
    try {
      await conn.beginTransaction();

      for (const w of workers) {
        const [result] = await conn.execute(`
          INSERT IGNORE INTO daily_attendance_records
          (record_date, user_id, status, created_by)
          VALUES (?, ?, 'incomplete', ?)
        `, [date, w.user_id, createdBy]);

        insertedCount += result.affectedRows;
      }

      await conn.commit();
    } catch (err) {
      await conn.rollback();
      throw err;
    } finally {
      conn.release();
    }

    return { inserted: insertedCount, total_active_workers: workers.length };
  }

  // 근태 기록 생성 또는 업데이트
  static async upsertAttendanceRecord(recordData) {
    const db = await getDb();

    const {
      record_date,
      user_id,
      total_work_hours = 8,
      work_attendance_type_id = 1,
      vacation_type_id = null,
      is_overtime_approved = false,
      created_by = 1
    } = recordData;

    const attendance_type_id = work_attendance_type_id;

    // 기존 기록 확인
    const [existing] = await db.execute(
      'SELECT id FROM daily_attendance_records WHERE user_id = ? AND record_date = ?',
      [user_id, record_date]
    );

    if (existing.length > 0) {
      // 업데이트
      const [result] = await db.execute(`
        UPDATE daily_attendance_records
        SET
          total_work_hours = ?,
          attendance_type_id = ?,
          vacation_type_id = ?,
          is_overtime_approved = ?,
          updated_at = CURRENT_TIMESTAMP
        WHERE id = ?
      `, [
        total_work_hours,
        attendance_type_id,
        vacation_type_id,
        is_overtime_approved,
        existing[0].id
      ]);

      return { id: existing[0].id, affected: result.affectedRows };
    } else {
      // 생성
      const [result] = await db.execute(`
        INSERT INTO daily_attendance_records (
          record_date, user_id, total_work_hours, attendance_type_id,
          vacation_type_id, is_overtime_approved, created_by
        ) VALUES (?, ?, ?, ?, ?, ?, ?)
      `, [
        record_date,
        user_id,
        total_work_hours,
        attendance_type_id,
        vacation_type_id,
        is_overtime_approved,
        created_by
      ]);

      return { id: result.insertId, affected: result.affectedRows };
    }
  }

  // 작업자별 근태 현황 조회 (대시보드용)
  static async getWorkerAttendanceStatus(date) {
    const db = await getDb();

    // 모든 작업자와 해당 날짜의 근태 기록을 조회
    const [rows] = await db.execute(`
      SELECT
        w.user_id,
        w.worker_name,
        w.job_type,
        COALESCE(dar.total_work_hours, 0) as total_work_hours,
        COALESCE(dar.status, 'incomplete') as status,
        dar.is_overtime_approved,
        wat.type_name as attendance_type_name,
        wat.type_code as attendance_type_code,
        vt.type_name as vacation_type_name,
        vt.type_code as vacation_type_code,
        dar.notes,
        -- 작업 건수 계산
        (SELECT COUNT(*) FROM daily_work_reports dwr
         WHERE dwr.user_id = w.user_id AND dwr.report_date = ?) as work_count,
        -- 오류 건수 계산
        (SELECT COUNT(*) FROM daily_work_reports dwr
         WHERE dwr.user_id = w.user_id AND dwr.report_date = ? AND dwr.work_status_id = 2) as error_count
      FROM workers w
      LEFT JOIN daily_attendance_records dar ON w.user_id = dar.user_id AND dar.record_date = ?
      LEFT JOIN work_attendance_types wat ON dar.attendance_type_id = wat.id
      LEFT JOIN vacation_types vt ON dar.vacation_type_id = vt.id
      WHERE w.status = 'active' AND w.user_id IS NOT NULL
      ORDER BY w.worker_name
    `, [date, date, date]);

    return rows;
  }

  // 휴가 처리
  static async processVacation(userId, date, vacationType, createdBy) {
    const db = await getDb();

    // 휴가 유형 정보 조회
    const [vacationTypes] = await db.execute(
      'SELECT id, type_code, type_name, deduct_days, is_active, created_at, updated_at FROM vacation_types WHERE type_code = ?',
      [vacationType]
    );

    if (vacationTypes.length === 0) {
      throw new Error('유효하지 않은 휴가 유형입니다.');
    }

    const vacationTypeInfo = vacationTypes[0];

    // 현재 작업 시간 조회
    const [workHours] = await db.execute(`
      SELECT COALESCE(SUM(work_hours), 0) as total_hours
      FROM daily_work_reports
      WHERE user_id = ? AND report_date = ?
    `, [userId, date]);

    const currentHours = parseFloat(workHours[0].total_hours);
    // deduct_days를 시간으로 변환 (1일 = 8시간)
    const vacationHours = parseFloat(vacationTypeInfo.deduct_days) * 8;
    const totalHours = currentHours + vacationHours;

    // 근로 유형 결정
    let attendanceTypeCode = 'VACATION';
    let status = 'vacation';

    if (totalHours >= 8) {
      attendanceTypeCode = totalHours > 8 ? 'OVERTIME' : 'REGULAR';
      status = totalHours > 8 ? 'overtime' : 'complete';
    }

    const [attendanceTypes] = await db.execute(
      'SELECT id FROM work_attendance_types WHERE type_code = ?',
      [attendanceTypeCode]
    );

    // 휴가 작업 기록 생성 (프로젝트 ID 13 = "연차/휴무", work_type_id 1 = 기본)
    await db.execute(`
      INSERT INTO daily_work_reports (
        report_date, user_id, project_id, work_type_id, work_status_id,
        work_hours, description, created_by
      ) VALUES (?, ?, 13, 1, 1, ?, ?, ?)
    `, [
      date,
      userId,
      vacationHours,
      `${vacationTypeInfo.type_name} 처리`,
      createdBy
    ]);

    // 근태 기록 업데이트
    const attendanceData = {
      record_date: date,
      user_id: userId,
      total_work_hours: totalHours,
      work_attendance_type_id: attendanceTypes[0]?.id,
      vacation_type_id: vacationTypeInfo.id,
      is_overtime_approved: false,
      created_by: createdBy
    };

    return await this.upsertAttendanceRecord(attendanceData);
  }

  // 초과근무 승인
  static async approveOvertime(userId, date, approvedBy) {
    const db = await getDb();

    const [result] = await db.execute(`
      UPDATE daily_attendance_records
      SET
        overtime_approved = TRUE,
        overtime_approved_by = ?,
        overtime_approved_at = CURRENT_TIMESTAMP,
        updated_by = ?,
        updated_at = CURRENT_TIMESTAMP
      WHERE user_id = ? AND record_date = ?
    `, [approvedBy, approvedBy, userId, date]);

    return result.affectedRows > 0;
  }

  // 근로 유형 목록 조회
  static async getAttendanceTypes() {
    const db = await getDb();
    const [rows] = await db.execute(
      'SELECT id, type_code, type_name, description, is_active, created_at, updated_at FROM work_attendance_types WHERE is_active = TRUE ORDER BY id'
    );
    return rows;
  }

  // 휴가 유형 목록 조회
  static async getVacationTypes() {
    const db = await getDb();
    const [rows] = await db.execute(
      'SELECT id, type_code, type_name, deduct_days, is_active, created_at, updated_at FROM vacation_types WHERE is_active = TRUE ORDER BY deduct_days DESC'
    );
    return rows;
  }

  // 작업자 휴가 잔여 조회
  static async getWorkerVacationBalance(userId, year = null) {
    const db = await getDb();
    const currentYear = year || new Date().getFullYear();

    const [rows] = await db.execute(`
      SELECT id, user_id, year, total_annual_leave, used_annual_leave, notes, created_at, updated_at FROM worker_vacation_balance
      WHERE user_id = ? AND year = ?
    `, [userId, currentYear]);

    if (rows.length === 0) {
      // 기본 연차 생성 (15일)
      await db.execute(`
        INSERT INTO worker_vacation_balance (user_id, year, total_annual_leave)
        VALUES (?, ?, 15.0)
      `, [userId, currentYear]);

      return {
        user_id: userId,
        year: currentYear,
        total_annual_leave: 15.0,
        used_annual_leave: 0,
        remaining_annual_leave: 15.0
      };
    }

    return rows[0];
  }

  // 월별 근태 통계
  static async getMonthlyAttendanceStats(year, month, userId = null) {
    const db = await getDb();

    // work_attendance_types: 1=NORMAL, 2=LATE, 3=EARLY_LEAVE, 4=ABSENT, 5=VACATION
    // vacation_types: 1=ANNUAL(연차), 2=HALF_ANNUAL(반차), 3=SICK(병가), 4=SPECIAL(경조사)
    let query = `
      SELECT
        w.user_id,
        w.worker_name,
        COUNT(CASE WHEN dar.attendance_type_id = 1 AND (dar.is_overtime_approved = 0 OR dar.is_overtime_approved IS NULL) THEN 1 END) as regular_days,
        COUNT(CASE WHEN dar.is_overtime_approved = 1 OR dar.total_work_hours > 8 THEN 1 END) as overtime_days,
        COUNT(CASE WHEN dar.attendance_type_id = 5 AND dar.vacation_type_id = 1 THEN 1 END) as vacation_days,
        COUNT(CASE WHEN dar.vacation_type_id = 2 THEN 1 END) as partial_days,
        COUNT(CASE WHEN dar.attendance_type_id = 4 THEN 1 END) as incomplete_days,
        COALESCE(SUM(dar.total_work_hours), 0) as total_work_hours,
        COALESCE(AVG(dar.total_work_hours), 0) as avg_work_hours
      FROM workers w
      LEFT JOIN daily_attendance_records dar ON w.user_id = dar.user_id
        AND YEAR(dar.record_date) = ? AND MONTH(dar.record_date) = ?
      WHERE w.employment_status = 'employed'
    `;

    const params = [year, month];

    if (userId) {
      query += ' AND w.user_id = ?';
      params.push(userId);
    }

    query += ' GROUP BY w.user_id, w.worker_name ORDER BY w.worker_name';

    const [rows] = await db.execute(query, params);
    return rows;
  }

  // 출근 체크 기록 생성 또는 업데이트
  static async upsertCheckin(checkinData) {
    const db = await getDb();
    const { user_id, record_date, is_present } = checkinData;

    // 해당 날짜에 기록이 있는지 확인
    const [existing] = await db.execute(
      'SELECT id FROM daily_attendance_records WHERE user_id = ? AND record_date = ?',
      [user_id, record_date]
    );

    if (existing.length > 0) {
      // 업데이트
      await db.execute(
        'UPDATE daily_attendance_records SET is_present = ? WHERE id = ?',
        [is_present, existing[0].id]
      );
      return existing[0].id;
    } else {
      // 새로 생성 (기본값으로)
      const [result] = await db.execute(
        `INSERT INTO daily_attendance_records
        (user_id, record_date, is_present, attendance_type_id, created_by)
        VALUES (?, ?, ?, 1, 1)`,
        [user_id, record_date, is_present]
      );
      return result.insertId;
    }
  }

  // 특정 날짜의 출근 체크 목록 조회 (휴가 정보 포함)
  static async getCheckinList(date) {
    const db = await getDb();
    const query = `
      SELECT
        w.user_id,
        w.worker_name,
        w.job_type,
        w.employment_status,
        COALESCE(dar.is_present, TRUE) as is_present,
        dar.id as record_id,
        vr.request_id as vacation_request_id,
        vr.status as vacation_status,
        vt.type_name as vacation_type_name,
        vt.type_code as vacation_type_code,
        vr.days_used as vacation_days
      FROM workers w
      LEFT JOIN daily_attendance_records dar
        ON w.user_id = dar.user_id AND dar.record_date = ?
      LEFT JOIN vacation_requests vr
        ON w.user_id = vr.user_id
        AND ? BETWEEN vr.start_date AND vr.end_date
        AND vr.status = 'approved'
      LEFT JOIN vacation_types vt ON vr.vacation_type_id = vt.id
      WHERE w.employment_status = 'employed'
      ORDER BY w.worker_name
    `;

    const [rows] = await db.execute(query, [date, date]);
    return rows;
  }
}

module.exports = AttendanceModel;
