import { parseTime } from "@/utils/tools";
import {
  queryAttendanceGroupByUserId,
  checkIn,
  checkOut,
  getCurrentDayRecord,
} from "@/api/oa/attendance/clockIn";

import i18n from "@/i18n";
import timeService from "@/utils/timeService";

// 打卡状态常量
export const PUNCH_STATUS = {
  NOT_CHECKED: 0,
  CHECKED_IN: 1,
  CHECKED_OUT: 2,
  LATE_IN: 3,
  EARLY_OUT: 4,
  UPDATE_OUT: 5,
};

// 打卡类型常量
export const PUNCH_TYPE = {
  CLOCK_IN: "clockIn",
  CLOCK_OUT: "clockOut",
};

/**
 * 考勤服务类
 * 处理所有考勤相关的业务逻辑
 */
export class AttendanceService {
  /**
   * 创建安全的 Date 对象（兼容 IOS 系统）
   * @param {string|number|Date} input 输入的日期参数
   * @param {boolean} useServerTime 是否使用服务器时间（默认为true）
   * @returns {Date} 有效的 Date 对象
   * @throws {Error} 当无法创建有效日期时抛出错误
   */
  static createSafeDate(input, useServerTime = true) {
    let date;

    if (!input) {
      // 如果没有输入参数，优先使用服务器时间
      date = useServerTime ? timeService.getCurrentTime() : new Date();
    } else if (input instanceof Date) {
      date = input;
    } else if (typeof input === "string") {
      // 处理 IOS 系统的日期格式兼容性问题
      // 将 'YYYY-MM-DD HH:mm:ss' 格式转换为 'YYYY/MM/DD HH:mm:ss'
      const normalizedInput = input.replace(/-/g, "/");
      date = new Date(normalizedInput);
    } else {
      date = new Date(input);
    }

    // 验证创建的日期是否有效
    if (!date || isNaN(date.getTime())) {
      throw new Error("无法创建有效的日期对象");
    }

    return date;
  }

  /**
   * 计算两个经纬度之间的距离
   * @param {number} lat1 纬度1
   * @param {number} lng1 经度1
   * @param {number} lat2 纬度2
   * @param {number} lng2 经度2
   * @returns {number} 距离，米
   */
  static calculateDistance(lat1, lng1, lat2, lng2) {
    const R = 6371000; // 地球半径，单位：米
    const dLat = (lat2 - lat1) * (Math.PI / 180);
    const dLng = (lng2 - lng1) * (Math.PI / 180);

    const a =
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(lat1 * (Math.PI / 180)) *
        Math.cos(lat2 * (Math.PI / 180)) *
        Math.sin(dLng / 2) *
        Math.sin(dLng / 2);

    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c; // 返回单位：米
  }

  /**
   * 判断是否在打卡范围内
   * @param {Object} userLocation 用户位置
   * @param {Object} attendanceGroup 考勤组信息
   * @returns {boolean} 是否在范围内
   */
  static isInAttendanceRange(userLocation, attendanceGroup) {
    const { latitude, longitude } = userLocation;
    const { lat, lng, radius } = attendanceGroup;

    if (!latitude || !longitude || !lat || !lng) {
      return false;
    }

    const distance = this.calculateDistance(latitude, longitude, lat, lng);
    return distance <= radius;
  }

  /**
   * 比较当前时间与计划时间（使用服务器时间）
   * @param {string} scheduleTime 计划时间 (HH:mm格式)
   * @returns {number} 比较结果 (-1: 早于, 0: 等于, 1: 晚于)
   */
  static compareTimeWithSchedule(scheduleTime) {
    return timeService.compareTimeWithSchedule(scheduleTime);
  }

  /**
   * 判断当前时间是否晚于上班时间
   * @param {string} workStartTime 上班时间
   * @returns {boolean} 是否迟到
   */
  static isLateForWork(workStartTime) {
    return this.compareTimeWithSchedule(workStartTime) > 0;
  }

  /**
   * 判断当前时间是否早于下班时间
   * @param {string} workEndTime 下班时间
   * @returns {boolean} 是否早退
   */
  static isEarlyForLeaving(workEndTime) {
    return this.compareTimeWithSchedule(workEndTime) < 0;
  }

  /**
   * 获取打卡状态文本
   * @param {number} status 状态码
   * @returns {string} 状态文本
   */
  static getStatusText(status) {
    const statusTexts = {
      [PUNCH_STATUS.NOT_CHECKED]: i18n.t("checkin.punch.status.notChecked"), // 未打卡
      [PUNCH_STATUS.CHECKED_IN]: i18n.t("checkin.punch.status.checkedIn"), // 已打卡
      [PUNCH_STATUS.CHECKED_OUT]: i18n.t("checkin.punch.status.checkedOut"), // 已打卡
      [PUNCH_STATUS.LATE_IN]: i18n.t("checkin.punch.status.lateIn"), // 迟到打卡
      [PUNCH_STATUS.EARLY_OUT]: i18n.t("checkin.punch.status.earlyOut"), // 早退打卡
      [PUNCH_STATUS.UPDATE_OUT]: i18n.t("checkin.punch.status.updateOut"), // 更新打卡
    };

    return statusTexts[status] || i18n.t("checkin.punch.status.notChecked"); // 未打卡
  }

  /**
   * 格式化日期时间
   * @param {Date} date 日期对象，如果不传则使用服务器当前时间
   * @returns {string} 格式化后的日期时间
   */
  static formatDateTime(date) {
    const targetDate = date || timeService.getCurrentTime();
    return parseTime(targetDate, "{y}-{m}-{d} {h}:{i}:{s}");
  }

  /**
   * 格式化时间显示
   * @param {string} time 时间字符串
   * @returns {string} 格式化后的时间
   */
  static formatTime(time) {
    return time ? parseTime(time, "{h}:{i}:{s}") : "";
  }

  /**
   * 计算日期信息
   * @param {Date} date 日期对象
   * @returns {Object} 包含月、周、日的对象
   * @throws {Error} 当传入的日期无效时抛出错误
   */
  static calculateDateInfo(date) {
    // 参数验证：确保传入的是有效的 Date 对象
    if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
      // 传入的日期参数无效，请确保是有效的 Date 对象
      throw new Error("传入的日期参数无效，请确保是有效的 Date 对象");
    }

    // 获取月份（1-12）
    const month = date.getMonth() + 1;

    // 获取日期（1-31）
    const day = date.getDate();

    // 计算是一年中的第几周（0-6，0为周日）
    const week = date.getDay();

    // 二次验证：确保返回值都是有效数字
    if (isNaN(month) || isNaN(day) || isNaN(week)) {
      // 日期计算结果异常，请检查传入的日期对象
      throw new Error("日期计算结果异常，请检查传入的日期对象");
    }

    // 范围验证：确保返回值在合理范围内
    if (
      month < 1 ||
      month > 12 ||
      day < 1 ||
      day > 31 ||
      week < 0 ||
      week > 6
    ) {
      // 日期计算结果超出有效范围
      throw new Error("日期计算结果超出有效范围");
    }

    return { month, week, day };
  }

  /**
   * 构建打卡请求数据
   * @param {Object} params 参数对象
   * @returns {Object} 打卡数据
   * @throws {Error} 当日期计算失败时抛出错误
   */
  static buildPunchData(params) {
    const {
      type,
      userInfo,
      userLocation,
      remark,
      attendanceGroupId,
      attendanceGroupName,
    } = params;

    const statusMap = {
      morning: PUNCH_STATUS.CHECKED_IN,
      evening: PUNCH_STATUS.CHECKED_OUT,
      morning_late: PUNCH_STATUS.LATE_IN,
      evening_early: PUNCH_STATUS.EARLY_OUT,
    };

    const isClockIn = type.includes("morning");

    // 使用安全的日期创建方法（优先使用服务器时间）
    const currentDate = this.createSafeDate();
    const currentTime = this.formatDateTime(currentDate);

    // 获取当前日期的年月日信息
    let month, week, day;
    try {
      const dateInfo = this.calculateDateInfo(currentDate);
      month = dateInfo.month;
      week = dateInfo.week;
      day = dateInfo.day;
    } catch (error) {
      // 如果日期计算失败，使用当前时间重新计算
      try {
        const fallbackDate = this.createSafeDate();
        const fallbackDateInfo = this.calculateDateInfo(fallbackDate);
        month = fallbackDateInfo.month;
        week = fallbackDateInfo.week;
        day = fallbackDateInfo.day;
      } catch (fallbackError) {
        // 如果仍然失败，抛出错误
        throw new Error(`日期计算失败: ${error.message}`);
      }
    }

    return {
      userId: userInfo.userId,
      userName: userInfo.userName,
      nickName: userInfo.nickName,
      lng: userLocation.longitude,
      lat: userLocation.latitude,
      checkInStatus: statusMap[type],
      checkInType: isClockIn ? PUNCH_TYPE.CLOCK_IN : PUNCH_TYPE.CLOCK_OUT,
      [isClockIn ? "clockIn" : "clockOut"]: currentTime,
      attendanceGroupId,
      attendanceGroupName,
      month,
      week,
      day,
      description: remark || "",
    };
  }

  /**
   * 处理考勤记录数据
   * @param {Array} records 考勤记录数组
   * @returns {Object} 处理后的考勤状态
   */
  static processAttendanceRecords(records) {
    const attendanceStatus = {
      clockInTime: null,
      clockOutTime: null,
      clockInStatus: PUNCH_STATUS.NOT_CHECKED,
      clockOutStatus: PUNCH_STATUS.NOT_CHECKED,
    };

    records.forEach((record) => {
      attendanceStatus.clockInTime = record.clockIn;
      attendanceStatus.clockInStatus = parseInt(record.clockInStatus);
      attendanceStatus.clockOutTime = record.clockOut;
      attendanceStatus.clockOutStatus = parseInt(record.clockOutStatus || 0);
    });

    return attendanceStatus;
  }

  /**
   * 获取考勤组信息
   * @param {string} userId 用户ID
   * @returns {Promise<Object>} 考勤组信息
   */
  static async getAttendanceGroup(userId) {
    const response = await queryAttendanceGroupByUserId({ userId });

    if (response.code !== 200) {
      // 获取考勤组信息失败
      throw new Error(response.msg || "获取考勤组信息失败");
    }

    if (!response.data) {
      // 未配置考勤组,请联系管理员
      throw new Error("未配置考勤组,请联系管理员");
    }

    return response.data;
  }

  /**
   * 获取今日考勤记录
   * @param {string} userId 用户ID
   * @returns {Promise<Array>} 考勤记录数组
   */
  static async getTodayAttendance(userId) {
    const response = await getCurrentDayRecord({ userId });

    if (response.code !== 200) {
      // 获取考勤状态失败
      throw new Error(response.msg || "获取考勤状态失败");
    }

    return response.data || [];
  }

  /**
   * 提交打卡
   * @param {Object} punchData 打卡数据
   * @returns {Promise<Object>} API响应
   */
  static async submitPunch(punchData) {
    const apiCall =
      punchData.checkInType === PUNCH_TYPE.CLOCK_IN ? checkIn : checkOut;
    const response = await apiCall(punchData);

    if (response.code !== 200) {
      throw new Error(response.msg || i18n.t("checkin.punch.status.error")); // 打卡失败
    }

    return response;
  }
}
