// src/client/entities/operations.ts
// Operations entity - Pinia store and types for daily check-in operations management

import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import {
  operationsRepository,
  type DailyCheckinResponse,
  type PatchDailyCheckinRequest,
  type DailyLinenUsageResponse,
  type PatchDailyLinenUsageRequest,
  type LinenDataItem,
  type SyncCheckinsResponse,
  type CleaningScheduleEntryResponse,
  type SyncCleaningScheduleResponse,
  type CleaningScheduleMonthlyResponse,
} from '../shared/api/repositories/OperationsRepository';
import type { Inventory } from './inventory';
import { useUserStore } from './user';
import { usePermissionsStore } from './permissions';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import { AppPermission } from '@shared/contracts/permissions';

// Расширяем dayjs плагинами
dayjs.extend(utc);
dayjs.extend(timezone);

// Хелпер для вычисления операционного дня
const getInitialOpDay = () => {
  const now = dayjs().tz('Europe/Moscow');
  return now.hour() < 9 ? now.subtract(1, 'day').format('YYYY-MM-DD') : now.format('YYYY-MM-DD');
};

// Re-export types for backward compatibility
export type DailyCheckin = DailyCheckinResponse;
export type { LinenDataItem };

export const useOperationsStore = defineStore('operations', () => {
  // State
  const checkins = ref<DailyCheckin[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);
  const selectedDate = ref<string>(getInitialOpDay());
  const dailyLinen = ref<DailyLinenUsageResponse | null>(null);
  const cleaningSchedule = ref<CleaningScheduleEntryResponse[]>([]);

  // Computed
  // activeCheckins removed - all checkins are now active (hard delete instead of archive)

  // Вычисляем текущий операционный день
  const currentOperationalDay = computed(() => {
    const now = dayjs().tz('Europe/Moscow');
    const hour = now.hour();

    // Если до 10:00 утра, операционный день - вчера
    if (hour < 9) {
      return now.subtract(1, 'day').format('YYYY-MM-DD');
    }

    // Иначе - сегодня
    return now.format('YYYY-MM-DD');
  });

  // Проверяем, является ли выбранная дата текущим операционным днем
  const isHistory = computed(() => selectedDate.value < currentOperationalDay.value);

  // Проверяем права редактирования
  const canEdit = computed(() => {
    const userStore = useUserStore();
    const permissionsStore = usePermissionsStore();

    if (userStore.isGod) {
      return true;
    }

    return (
      permissionsStore.hasPermission(AppPermission.CHECKIN_WRITE)
      || permissionsStore.hasPermission(AppPermission.CHECKIN_MANAGE)
    );
  });

  // Actions
  async function fetchCheckins(date: string) {
    loading.value = true;
    error.value = null;
    try {
      const data = await operationsRepository.getDailyCheckinsByDate(date);
      checkins.value = data;
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить данные';
      console.error('[OperationsStore] Error fetching checkins:', err);
      // Don't throw - allow component to render with error state
    } finally {
      loading.value = false;
    }
  }

  async function fetchCheckinById(id: string) {
    loading.value = true;
    error.value = null;
    try {
      const data = await operationsRepository.getDailyCheckinById(id);
      // Update or add checkin to list
      const index = checkins.value.findIndex((c) => c.id === id);
      if (index >= 0) {
        checkins.value[index] = data;
      } else {
        checkins.value.push(data);
      }
      return data;
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить запись';
      console.error('[OperationsStore] Error fetching checkin by ID:', err);
      // Don't throw - allow component to render with error state
      return null;
    } finally {
      loading.value = false;
    }
  }

  /**
   * Updates a checkin with optimistic UI
   * - Optimistically updates local state
   * - Calls API to persist changes
   * - On error, rolls back the state
   */
  async function updateCheckin(id: string, updates: Partial<DailyCheckin>) {
    // Find the checkin to update
    const index = checkins.value.findIndex((c) => c.id === id);
    if (index === -1) {
      throw new Error('Checkin not found');
    }

    // Store previous state for rollback
    const previousState = { ...checkins.value[index] };

    // Optimistically update local state
    checkins.value[index] = { ...checkins.value[index], ...updates };

    try {
      // Prepare patch data (only send fields that changed)
      const patchData: PatchDailyCheckinRequest = {};
      
      if (updates.isOccupied !== undefined) {
        patchData.isOccupied = updates.isOccupied;
      }
      if (updates.isPermanent !== undefined) {
        patchData.isPermanent = updates.isPermanent;
      }
      if (updates.bedsCount !== undefined) {
        patchData.bedsCount = updates.bedsCount;
      }
      if (updates.isCleaned !== undefined) {
        patchData.isCleaned = updates.isCleaned;
      }
      if (updates.cleanerName !== undefined) {
        patchData.cleanerName = updates.cleanerName || undefined;
      }
      if (updates.isCheckin !== undefined) {
        patchData.isCheckin = updates.isCheckin;
      }
      if (updates.isCheckout !== undefined) {
        patchData.isCheckout = updates.isCheckout;
      }
      if (updates.isLinenChanged !== undefined) {
        patchData.isLinenChanged = updates.isLinenChanged;
      }
      if (updates.comment !== undefined) {
        const normalizedComment = typeof updates.comment === 'string'
          ? updates.comment.trim()
          : updates.comment;

        patchData.comment = normalizedComment === '' ? null : normalizedComment;
      }
      if (updates.linenData !== undefined) {
        patchData.linenData = updates.linenData || [];
      }

      // Call API
      const result = await operationsRepository.patchDailyCheckin(id, patchData);

      // Update with server response
      checkins.value[index] = result;

      return result;
    } catch (err: any) {
      // Rollback on error
      checkins.value[index] = previousState;
      error.value = err.message || 'Не удалось обновить запись';
      throw err;
    }
  }

  /**
   * Adds a linen item to a room's linen data
   * - Appends a new snapshot object to linenData JSON
   * - Saves via updateCheckin
   */
  async function addLinenToRoom(checkinId: string, inventoryItem: Inventory) {
    const checkin = checkins.value.find((c) => c.id === checkinId);
    if (!checkin) {
      throw new Error('Checkin not found');
    }

    // Use weightPerUnit from inventory and ensure it's a number
    const weight = Number(inventoryItem.weightPerUnit) || 0;

    // Create new linen item with snapshot data
    const newLinenItem: LinenDataItem = {
      inventoryId: inventoryItem.id,
      name: inventoryItem.name,
      weight,
      quantity: 1,
    };

    // Get current linen data or initialize as empty array
    const currentLinenData = checkin.linenData || [];
    
    // Check if item already exists, increment quantity
    const existingItemIndex = currentLinenData.findIndex(
      (item) => item.inventoryId === inventoryItem.id
    );

    let updatedLinenData: LinenDataItem[];
    
    if (existingItemIndex >= 0) {
      // Increment quantity of existing item
      updatedLinenData = [...currentLinenData];
      updatedLinenData[existingItemIndex] = {
        ...updatedLinenData[existingItemIndex],
        quantity: updatedLinenData[existingItemIndex].quantity + 1,
      };
    } else {
      // Add new item
      updatedLinenData = [...currentLinenData, newLinenItem];
    }

    // Update checkin with new linen data
    await updateCheckin(checkinId, { linenData: updatedLinenData });
  }

  /**
   * Removes a linen item from a room's linen data
   */
  async function removeLinenFromRoom(checkinId: string, inventoryId: string) {
    const checkin = checkins.value.find((c) => c.id === checkinId);
    if (!checkin) {
      throw new Error('Checkin not found');
    }

    const currentLinenData = checkin.linenData || [];
    
    // Find the item
    const existingItemIndex = currentLinenData.findIndex(
      (item) => item.inventoryId === inventoryId
    );

    if (existingItemIndex === -1) {
      return; // Item not found, nothing to do
    }

    let updatedLinenData: LinenDataItem[];
    
    if (currentLinenData[existingItemIndex].quantity > 1) {
      // Decrement quantity
      updatedLinenData = [...currentLinenData];
      updatedLinenData[existingItemIndex] = {
        ...updatedLinenData[existingItemIndex],
        quantity: updatedLinenData[existingItemIndex].quantity - 1,
      };
    } else {
      // Remove item completely
      updatedLinenData = currentLinenData.filter(
        (item) => item.inventoryId !== inventoryId
      );
    }

    // Update checkin with new linen data
    await updateCheckin(checkinId, { linenData: updatedLinenData });
  }

  /**
   * Sets the selected date and fetches checkins for that date
   */
  async function setDate(date: string) {
    selectedDate.value = date;
    await fetchCheckins(date);
  }

  function clearError() {
    error.value = null;
  }

  /**
    * Placeholder action for fetching linen usage report
    * This will be used for the final reporting feature
    * @param from - Start date in YYYY-MM-DD format
    * @param to - End date in YYYY-MM-DD format
    */
  async function fetchLinenReport(from: string, to: string) {
    loading.value = true;
    error.value = null;
    try {
      // TODO: Implement this once the backend reporting endpoint is ready
      // const data = await operationsRepository.getLinenUsageReport(from, to);
      console.log(`[OperationsStore] Fetching linen report from ${from} to ${to}`);
      // Return placeholder data for now
      return {
        items: [],
        grandTotalWeight: 0,
      };
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить отчет';
      console.error('[OperationsStore] Error fetching linen report:', err);
      throw err;
    } finally {
      loading.value = false;
    }
  }

  /**
    * Fetches daily linen usage for the selected date
    */
  async function fetchDailyLinen(date: string) {
    loading.value = true;
    error.value = null;
    try {
      const data = await operationsRepository.getDailyLinenUsageByDate(date);
      dailyLinen.value = data;
      return data;
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить данные о белье';
      console.error('[OperationsStore] Error fetching daily linen:', err);
      throw err;
    } finally {
      loading.value = false;
    }
  }

  /**
    * Saves daily linen usage for the selected date
    */
  async function saveDailyLinen(linenData: LinenDataItem[]) {
    loading.value = true;
    error.value = null;
    try {
      const data: PatchDailyLinenUsageRequest = {
        date: selectedDate.value,
        linenData,
      };
      const result = await operationsRepository.upsertDailyLinenUsage(data);
      dailyLinen.value = result;
      return result;
    } catch (err: any) {
      error.value = err.message || 'Не удалось сохранить данные о белье';
      console.error('[OperationsStore] Error saving daily linen:', err);
      throw err;
    } finally {
      loading.value = false;
    }
  }

  /**
   * Синхронизирует daily_checkins с актуальным списком номеров из rooms
   * Удаляет неактуальные снапшоты и создает новые для текущего дня
   */
  async function syncRooms(date: string): Promise<SyncCheckinsResponse> {
    loading.value = true;
    error.value = null;
    try {
      const result = await operationsRepository.syncRooms(date);
      // Refresh checkins after sync
      await fetchCheckins(date);
      return result;
    } catch (err: any) {
      error.value = err.message || 'Не удалось синхронизировать номера';
      console.error('[OperationsStore] Error syncing rooms:', err);
      throw err;
    } finally {
      loading.value = false;
    }
  }

  // ==================== Cleaning Schedule ====================

  const canManageCleaning = computed(() => {
    const userStore = useUserStore();
    const permissionsStore = usePermissionsStore();
    if (userStore.isGod) return true;
    return permissionsStore.hasPermission(AppPermission.CLEANING_MANAGE);
  });

  async function fetchCleaningSchedule(date: string) {
    loading.value = true;
    error.value = null;
    try {
      cleaningSchedule.value = await operationsRepository.getCleaningScheduleByDate(date);
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить график уборки';
    } finally {
      loading.value = false;
    }
  }

  async function addCleaningEntry(roomId: string, date: string, roomType: string, bedsCount: number) {
    try {
      await operationsRepository.addCleaningEntry({ roomId, date, roomType, bedsCount });
      await fetchCleaningSchedule(date);
    } catch (err: any) {
      error.value = err.message || 'Не удалось добавить номер в график уборки';
      throw err;
    }
  }

  async function removeCleaningEntry(id: string) {
    try {
      await operationsRepository.deleteCleaningEntry(id);
      cleaningSchedule.value = cleaningSchedule.value.filter(e => e.id !== id);
    } catch (err: any) {
      error.value = err.message || 'Не удалось удалить номер из графика уборки';
      throw err;
    }
  }

  async function patchCleaningEntry(id: string, data: { isCleaned?: boolean; cleanerName?: string; comment?: string }) {
    try {
      const updated = await operationsRepository.patchCleaningEntry(id, data);
      const idx = cleaningSchedule.value.findIndex(e => e.id === id);
      if (idx !== -1) cleaningSchedule.value[idx] = updated;
    } catch (err: any) {
      error.value = err.message || 'Не удалось обновить запись графика уборки';
      throw err;
    }
  }

  async function syncCleaningFromCheckins(date: string): Promise<SyncCleaningScheduleResponse> {
    try {
      const result = await operationsRepository.syncCleaningFromCheckins(date);
      await fetchCleaningSchedule(date);
      return result;
    } catch (err: any) {
      error.value = err.message || 'Не удалось синхронизировать график уборки';
      throw err;
    }
  }

  const cleaningMonthlyData = ref<CleaningScheduleMonthlyResponse | null>(null);

  const cleaningSelectedMonth = ref<number>(dayjs().tz('Europe/Moscow').month() + 1);
  const cleaningSelectedYear = ref<number>(dayjs().tz('Europe/Moscow').year());

  async function fetchCleaningScheduleMonthly(month?: number, year?: number) {
    loading.value = true;
    error.value = null;
    try {
      const m = month ?? cleaningSelectedMonth.value;
      const y = year ?? cleaningSelectedYear.value;
      cleaningMonthlyData.value = await operationsRepository.getCleaningScheduleByMonth(m, y);
      cleaningSelectedMonth.value = m;
      cleaningSelectedYear.value = y;
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить график уборки за месяц';
    } finally {
      loading.value = false;
    }
  }

  async function syncCleaningRooms(month?: number, year?: number): Promise<{ added: number }> {
    loading.value = true;
    error.value = null;
    try {
      const m = month ?? cleaningSelectedMonth.value;
      const y = year ?? cleaningSelectedYear.value;
      const result = await operationsRepository.syncCleaningRooms(m, y);
      await fetchCleaningScheduleMonthly(m, y);
      return result;
    } catch (err: any) {
      error.value = err.message || 'Не удалось синхронизировать';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function addRoomToCleaningSchedule(roomId: string, roomType: string, beds: number, month?: number, year?: number) {
    const m = month ?? cleaningSelectedMonth.value;
    const y = year ?? cleaningSelectedYear.value;
    await operationsRepository.addRoomToCleaningSchedule({
      month: m,
      year: y,
      roomId,
      roomType,
      beds,
    });
    await fetchCleaningScheduleMonthly(m, y);
  }

  async function removeRoomFromCleaningSchedule(roomId: string, month?: number, year?: number) {
    const m = month ?? cleaningSelectedMonth.value;
    const y = year ?? cleaningSelectedYear.value;
    await operationsRepository.removeRoomFromCleaningSchedule(m, y, roomId);
    await fetchCleaningScheduleMonthly(m, y);
  }

  async function toggleCleaningCell(roomId: string, date: string, roomType: string, bedsCount: number) {
    const entry = cleaningMonthlyData.value?.entries.find(
      (e) => e.roomId === roomId && e.date === date,
    );
    if (entry) {
      await operationsRepository.deleteCleaningEntry(entry.id);
      if (cleaningMonthlyData.value) {
        cleaningMonthlyData.value.entries = cleaningMonthlyData.value.entries.filter(
          (e) => !(e.roomId === roomId && e.date === date),
        );
      }
    } else {
      const result = await operationsRepository.addCleaningEntry({
        roomId,
        date,
        roomType,
        bedsCount,
      });
      if (cleaningMonthlyData.value) {
        cleaningMonthlyData.value.entries.push({
          id: result.id,
          roomId,
          date,
          roomType,
          bedsCount,
          isCleaned: false,
          cleanerName: null,
          comment: null,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
        });
      }
    }
  }

  async function patchCleaningCell(id: string, data: { isCleaned?: boolean; cleanerName?: string; comment?: string }) {
    try {
      const updated = await operationsRepository.patchCleaningEntry(id, data);
      if (cleaningMonthlyData.value) {
        const idx = cleaningMonthlyData.value.entries.findIndex((e) => e.id === id);
        if (idx !== -1) cleaningMonthlyData.value.entries[idx] = updated;
      }
    } catch (err: any) {
      error.value = err.message || 'Не удалось обновить запись';
      throw err;
    }
  }

  return {
    // State
    checkins,
    loading,
    error,
    selectedDate,
    dailyLinen,
    cleaningSchedule,
    cleaningMonthlyData,
    cleaningSelectedMonth,
    cleaningSelectedYear,
    // Computed
    currentOperationalDay,
    isHistory,
    canEdit,
    canManageCleaning,
    // Actions
    fetchCheckins,
    fetchCheckinById,
    updateCheckin,
    addLinenToRoom,
    removeLinenFromRoom,
    setDate,
    clearError,
    fetchLinenReport,
    fetchDailyLinen,
    saveDailyLinen,
    syncRooms,
    fetchCleaningSchedule,
    addCleaningEntry,
    removeCleaningEntry,
    patchCleaningEntry,
    syncCleaningFromCheckins,
    fetchCleaningScheduleMonthly,
    syncCleaningRooms,
    addRoomToCleaningSchedule,
    removeRoomFromCleaningSchedule,
    toggleCleaningCell,
    patchCleaningCell,
  };
});
