import type { Ref } from 'vue';

/**
 * 文件职责：排班编辑动作。
 * 1. 管理个人排班抽屉与周排班抽屉。
 * 2. 提交个人排班与周排班保存请求。
 */
import type {
  ShiftType,
  StaffDayShiftDto,
  StoreShiftTemplatesDto,
  StoreStaffDto,
} from '#/api/store-staff';
import type {
  StaffPersonalScheduleFormState,
  WeekEditorRow,
} from '#/views/store/staff/types';

import { message } from 'ant-design-vue';

import {
  saveStoreStaffPersonalScheduleApi,
  saveStoreStaffWeeklyScheduleApi,
} from '#/api/store-staff';

import { SHIFT_CYCLE } from './constants';
import {
  buildDayShiftByType,
  buildWeekEditorRows,
  cloneScheduleMap,
  cloneShifts,
  normalizeDayShift,
  normalizeSparseWeekShifts,
  normalizeTime,
  updateWeekRowShift,
} from './helpers';

interface CreateScheduleActionsOptions {
  isPersonalDrawerOpen: Ref<boolean>;
  isPersonalSaving: Ref<boolean>;
  isWeekDrawerOpen: Ref<boolean>;
  isWeekSaving: Ref<boolean>;
  loadScheduleSettings: () => Promise<void>;
  patchScheduleMap: (nextMap: Record<string, StaffDayShiftDto[]>) => void;
  patchStaffSchedule: (staffId: string, shifts: StaffDayShiftDto[]) => void;
  personalForm: StaffPersonalScheduleFormState;
  scheduleMap: Ref<Record<string, StaffDayShiftDto[]>>;
  selectedStoreId: Ref<string>;
  staffDirectory: Ref<StoreStaffDto[]>;
  templates: Ref<StoreShiftTemplatesDto>;
  weekRows: Ref<WeekEditorRow[]>;
}

export function createScheduleActions(options: CreateScheduleActionsOptions) {
  /** 打开个人排班抽屉。 */
  function openPersonalScheduleDrawer(staff: StoreStaffDto) {
    options.personalForm.staffId = staff.id;
    options.personalForm.staffName = staff.name;
    options.personalForm.roleType = staff.roleType;
    options.personalForm.shifts = cloneShifts(
      options.scheduleMap.value[staff.id] ?? [],
    );
    options.isPersonalDrawerOpen.value = true;
  }

  /** 按天更新个人排班（不存在时自动补入）。 */
  function upsertPersonalShift(dayOfWeek: number, nextShift: StaffDayShiftDto) {
    options.personalForm.shifts = [
      ...options.personalForm.shifts
        .filter((item) => item.dayOfWeek !== dayOfWeek)
        .map((item) => ({ ...item })),
      nextShift,
    ].toSorted((a, b) => a.dayOfWeek - b.dayOfWeek);
  }

  /** 更新个人排班班次类型。 */
  function setPersonalShiftType(dayOfWeek: number, shiftType: ShiftType) {
    const nextShift = buildDayShiftByType({
      dayOfWeek,
      shiftType,
      templates: options.templates.value,
    });
    upsertPersonalShift(dayOfWeek, nextShift);
  }

  /** 更新个人排班开始时间。 */
  function setPersonalShiftStart(dayOfWeek: number, startTime: string) {
    const currentShift = options.personalForm.shifts.find(
      (item) => item.dayOfWeek === dayOfWeek,
    );
    if (!currentShift || currentShift.shiftType === 'off') return;

    const nextShift = normalizeDayShift({
      dayOfWeek,
      shift: {
        ...currentShift,
        startTime: normalizeTime(startTime, currentShift.startTime),
      },
      fallback: currentShift,
      templates: options.templates.value,
    });
    upsertPersonalShift(dayOfWeek, nextShift);
  }

  /** 更新个人排班结束时间。 */
  function setPersonalShiftEnd(dayOfWeek: number, endTime: string) {
    const currentShift = options.personalForm.shifts.find(
      (item) => item.dayOfWeek === dayOfWeek,
    );
    if (!currentShift || currentShift.shiftType === 'off') return;

    const nextShift = normalizeDayShift({
      dayOfWeek,
      shift: {
        ...currentShift,
        endTime: normalizeTime(endTime, currentShift.endTime),
      },
      fallback: currentShift,
      templates: options.templates.value,
    });
    upsertPersonalShift(dayOfWeek, nextShift);
  }

  /** 提交个人排班。 */
  async function handleSavePersonalSchedule() {
    if (!options.selectedStoreId.value || !options.personalForm.staffId) return;

    options.isPersonalSaving.value = true;
    try {
      const result = await saveStoreStaffPersonalScheduleApi({
        storeId: options.selectedStoreId.value,
        staffId: options.personalForm.staffId,
        shifts: cloneShifts(options.personalForm.shifts),
      });

      options.patchStaffSchedule(
        result.staffId,
        normalizeSparseWeekShifts({
          shifts: result.shifts,
          templates: options.templates.value,
        }),
      );
      options.isPersonalDrawerOpen.value = false;
      message.success('员工排班已保存');
    } catch (error) {
      console.error(error);
    } finally {
      options.isPersonalSaving.value = false;
    }
  }

  /** 打开周排班抽屉。 */
  function openWeekScheduleDrawer() {
    const editableStaffs = options.staffDirectory.value.filter(
      (staff) => staff.status !== 'resigned',
    );
    options.weekRows.value = buildWeekEditorRows({
      staffs: editableStaffs,
      scheduleMap: options.scheduleMap.value,
    });
    options.isWeekDrawerOpen.value = true;
  }

  /** 按循环顺序切换周视图单元格班次。 */
  function cycleWeekShift(payload: { dayOfWeek: number; staffId: string }) {
    options.weekRows.value = options.weekRows.value.map((row) => {
      if (row.staffId !== payload.staffId) return row;

      const currentShift = row.shifts.find(
        (item) => item.dayOfWeek === payload.dayOfWeek,
      );
      const currentType = currentShift?.shiftType ?? 'off';
      const currentIndex = SHIFT_CYCLE.indexOf(currentType);
      const nextType =
        SHIFT_CYCLE[(currentIndex + 1) % SHIFT_CYCLE.length] ?? 'off';

      return updateWeekRowShift({
        row,
        dayOfWeek: payload.dayOfWeek,
        nextShiftType: nextType,
        templates: options.templates.value,
      });
    });
  }

  /** 提交周排班。 */
  async function handleSaveWeekSchedule() {
    if (!options.selectedStoreId.value) return;

    options.isWeekSaving.value = true;
    try {
      const schedules = options.weekRows.value.map((row) => ({
        staffId: row.staffId,
        shifts: cloneShifts(row.shifts),
      }));

      const result = await saveStoreStaffWeeklyScheduleApi({
        storeId: options.selectedStoreId.value,
        schedules,
      });

      const nextMap = cloneScheduleMap(options.scheduleMap.value);
      for (const schedule of result.schedules ?? []) {
        nextMap[schedule.staffId] = normalizeSparseWeekShifts({
          shifts: cloneShifts(schedule.shifts),
          templates: options.templates.value,
        });
      }
      options.patchScheduleMap(nextMap);

      options.isWeekDrawerOpen.value = false;
      message.success('本周排班已保存');

      // 1. 保守刷新一次后端快照，避免本地与服务端算法差异。
      await options.loadScheduleSettings();
    } catch (error) {
      console.error(error);
    } finally {
      options.isWeekSaving.value = false;
    }
  }

  return {
    cycleWeekShift,
    handleSavePersonalSchedule,
    handleSaveWeekSchedule,
    openPersonalScheduleDrawer,
    openWeekScheduleDrawer,
    setPersonalShiftEnd,
    setPersonalShiftStart,
    setPersonalShiftType,
  };
}
