// 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,
} from '../shared/api/repositories/OperationsRepository';
import type { Inventory } from './inventory';
import { useUserStore } from './user';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

// Расширяем 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);

  // 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();
    return !isHistory.value || userStore.isAdmin;
  });

  // 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.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
    const weight = 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;
    }
  }

  return {
    // State
    checkins,
    loading,
    error,
    selectedDate,
    dailyLinen,
    // Computed
    currentOperationalDay,
    isHistory,
    canEdit,
    // Actions
    fetchCheckins,
    fetchCheckinById,
    updateCheckin,
    addLinenToRoom,
    removeLinenFromRoom,
    setDate,
    clearError,
    fetchLinenReport,
    fetchDailyLinen,
    saveDailyLinen,
    syncRooms,
  };
});
