// src/client/shared/api/repositories/OperationsRepository.ts
// Operations Repository for check-in and laundry management operations with Zod validation

import { z } from 'zod';
import { BaseRepository, type RepositoryConfig } from '../BaseRepository';
import type { RequestConfig } from '../api.client';

/**
 * Linen Type Enum
 */
export const linenTypeEnum = z.enum(['SHEETS', 'TOWELS', 'PILLOWCASES', 'BLANKETS', 'OTHER']);

export type LinenType = z.infer<typeof linenTypeEnum>;

/**
 * Cleaning Status Enum
 */
export const cleaningStatusEnum = z.enum(['DIRTY', 'CLEAN', 'IN_PROGRESS']);

export type CleaningStatus = z.infer<typeof cleaningStatusEnum>;

/**
 * Linen Data Item Schema
 */
export const linenDataItemSchema = z.object({
  inventoryId: z.string().uuid(),
  name: z.string(),
  weight: z.number().nonnegative(),
  quantity: z.number().int().min(0),
});

export type LinenDataItem = z.infer<typeof linenDataItemSchema>;

/**
 * Daily Check-in Response Schema
 */
export const dailyCheckinResponseSchema = z.object({
  id: z.string().uuid(),
  roomId: z.string(), // Room ID (e.g., "A1", "B12")
  roomType: z.string(), // Snapshot data from rooms table
  date: z.string(),
  isOccupied: z.boolean(),
  isPermanent: z.boolean(),
  bedsCount: z.number().int(),
  isCheckin: z.boolean(),
  isCheckout: z.boolean(),
  isCleaned: z.boolean(),
  cleanerName: z.string().nullable(),
  isLinenChanged: z.boolean(),
  comment: z.string().nullable(),
  linenData: z.array(z.any()).nullable().default([]), // Relaxed: Accept any array structure
  isDispatched: z.boolean(),
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});

export type DailyCheckinResponse = z.infer<typeof dailyCheckinResponseSchema>;

/**
 * Daily Check-in List Response Schema
 */
export const dailyCheckinListResponseSchema = z.array(dailyCheckinResponseSchema);

/**
 * Patch Daily Check-in Request Schema
 */
export const patchDailyCheckinRequestSchema = z.object({
  isOccupied: z.boolean().optional(),
  isPermanent: z.boolean().optional(),
  bedsCount: z.number().int().min(0).optional(),
  isCheckin: z.boolean().optional(),
  isCheckout: z.boolean().optional(),
  isCleaned: z.boolean().optional(),
  cleanerName: z.string().max(255).optional(),
  isLinenChanged: z.boolean().optional(),
  comment: z.string().max(500).nullable().optional(),
  linenData: z.array(linenDataItemSchema).optional(),
});

export type PatchDailyCheckinRequest = z.infer<typeof patchDailyCheckinRequestSchema>;

/**
 * Inventory Linen Response Schema
 */
export const invLinenResponseSchema = z.object({
  id: z.string(),
  name: z.string(),
  type: linenTypeEnum,
  weight: z.string(), // decimal as string from database
  unit: z.string(),
  description: z.string().nullable(),
  createdAt: z.string(),
  updatedAt: z.string(),
  archivedAt: z.string().nullable(),
});

export type InvLinenResponse = z.infer<typeof invLinenResponseSchema>;

/**
 * Inventory Linen List Response Schema
 */
export const invLinenListResponseSchema = z.array(invLinenResponseSchema);

/**
 * Linen Dispatch Log Response Schema
 */
export const linenDispatchLogResponseSchema = z.object({
  id: z.string(),
  dispatchDate: z.string(), // YYYY-MM-DD format
  totalWeight: z.number().nonnegative(),
  details: z.array(z.object({
    roomId: z.string(),
    roomNumber: z.string(),
    items: z.array(linenDataItemSchema),
    totalWeight: z.number().nonnegative(),
  })),
  dispatchedAt: z.string(),
  createdAt: z.string(),
});

export type LinenDispatchLogResponse = z.infer<typeof linenDispatchLogResponseSchema>;

/**
 * Dispatch Laundry Request Schema
 */
export const dispatchLaundryRequestSchema = z.object({
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), // YYYY-MM-DD format
});

export type DispatchLaundryRequest = z.infer<typeof dispatchLaundryRequestSchema>;

/**
 * Dispatch Laundry Response Schema
 */
export const dispatchLaundryResponseSchema = z.object({
  dispatchId: z.string().nullable(),
  message: z.string(),
});

export type DispatchLaundryResponse = z.infer<typeof dispatchLaundryResponseSchema>;

/**
 * Daily Linen Usage Response Schema
 */
export const dailyLinenUsageResponseSchema = z.object({
  id: z.string().uuid().nullable().optional(),
  date: z.string(), // YYYY-MM-DD format
  linenData: z.array(linenDataItemSchema).default([]),
  updatedAt: z.string().datetime().nullable().optional(),
});

export type DailyLinenUsageResponse = z.infer<typeof dailyLinenUsageResponseSchema>;

/**
 * Patch Daily Linen Usage Request Schema
 */
export const patchDailyLinenUsageRequestSchema = z.object({
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), // YYYY-MM-DD format
  linenData: z.array(linenDataItemSchema),
});

export type PatchDailyLinenUsageRequest = z.infer<typeof patchDailyLinenUsageRequestSchema>;

/**
 * Sync Checkins Request Schema
 */
export const syncCheckinsRequestSchema = z.object({
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), // YYYY-MM-DD format
});

export type SyncCheckinsRequest = z.infer<typeof syncCheckinsRequestSchema>;

/**
 * Sync Checkins Response Schema
 */
export const syncCheckinsResponseSchema = z.object({
  added: z.number().int().nonnegative(),
  deleted: z.number().int().nonnegative(),
});

export type SyncCheckinsResponse = z.infer<typeof syncCheckinsResponseSchema>;

export const cleaningScheduleEntryResponseSchema = z.object({
  id: z.string().uuid(),
  date: z.string(),
  roomId: z.string(),
  roomType: z.string(),
  bedsCount: z.number().int(),
  isCleaned: z.boolean(),
  cleanerName: z.string().nullable(),
  comment: z.string().nullable(),
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});

export type CleaningScheduleEntryResponse = z.infer<typeof cleaningScheduleEntryResponseSchema>;

export const cleaningScheduleListResponseSchema = z.array(cleaningScheduleEntryResponseSchema);

export const cleaningMonthlyRoomSchema = z.object({
  id: z.string(),
  type: z.string(),
  beds: z.number().int(),
});

export const cleaningScheduleMonthlyResponseSchema = z.object({
  rooms: z.array(cleaningMonthlyRoomSchema),
  entries: z.array(cleaningScheduleEntryResponseSchema),
});

export type CleaningScheduleMonthlyResponse = z.infer<typeof cleaningScheduleMonthlyResponseSchema>;

export const syncCleaningScheduleResponseSchema = z.object({
  added: z.number().int().nonnegative(),
});

export type SyncCleaningScheduleResponse = z.infer<typeof syncCleaningScheduleResponseSchema>;

/**
 * Operations Repository
 */
export class OperationsRepository extends BaseRepository {
  protected basePath = '/operations';

  /**
   * Получает все чекины за дату (с кэшированием)
   */
  async getDailyCheckinsByDate(date: string): Promise<DailyCheckinResponse[]> {
    const config: RequestConfig<DailyCheckinResponse[]> = {
      schema: dailyCheckinListResponseSchema,
      params: { date },
    };

    return this.get<DailyCheckinResponse[]>('/checkins', config);
  }

  /**
   * Получает чекин по ID (с кэшированием)
   */
  async getDailyCheckinById(id: string): Promise<DailyCheckinResponse> {
    const config: RequestConfig<DailyCheckinResponse> = {
      schema: dailyCheckinResponseSchema,
    };

    return this.get<DailyCheckinResponse>(`/checkins/${id}`, config);
  }

  /**
   * Обновляет чекин (частичное обновление)
   */
  async patchDailyCheckin(id: string, data: PatchDailyCheckinRequest): Promise<DailyCheckinResponse> {
    const config: RequestConfig<DailyCheckinResponse> = {
      schema: dailyCheckinResponseSchema,
    };

    // Инвалидируем кэш после обновления
    const result = await this.patch<PatchDailyCheckinRequest, DailyCheckinResponse>(
      `/checkins/${id}`,
      data,
      config
    );
    this.invalidateCache();
    return result;
  }

  /**
   * Получает все типы белья (с кэшированием)
   */
  async getLinenTypes(): Promise<InvLinenResponse[]> {
    const config: RequestConfig<InvLinenResponse[]> = {
      schema: invLinenListResponseSchema,
    };

    return this.get<InvLinenResponse[]>('/linen-types', config);
  }

  /**
   * Получает тип белья по ID (с кэшированием)
   */
  async getLinenTypeById(id: string): Promise<InvLinenResponse> {
    const config: RequestConfig<InvLinenResponse> = {
      schema: invLinenResponseSchema,
    };

    return this.get<InvLinenResponse>(`/linen-types/${id}`, config);
  }

  /**
   * Отправляет белье в стирку
   */
  async dispatchLaundry(data: DispatchLaundryRequest): Promise<DispatchLaundryResponse> {
    const config: RequestConfig<DispatchLaundryResponse> = {
      schema: dispatchLaundryResponseSchema,
    };

    // Инвалидируем кэш после отправки
    const result = await this.post<DispatchLaundryRequest, DispatchLaundryResponse>(
      '/laundry/dispatch',
      data,
      config
    );
    this.invalidateCache();
    return result;
  }

  /**
   * Получает лог отправки белья за дату (с кэшированием)
   */
  async getLinenDispatchLogByDate(date: string): Promise<LinenDispatchLogResponse | null> {
    const config: RequestConfig<LinenDispatchLogResponse> = {
      schema: linenDispatchLogResponseSchema,
      params: { date },
    };

    return this.get<LinenDispatchLogResponse>('/laundry/dispatch-log', config);
  }

  /**
   * Получает ежедневное использование белья за дату (с кэшированием)
   */
  async getDailyLinenUsageByDate(date: string): Promise<DailyLinenUsageResponse | null> {
    const config: RequestConfig<DailyLinenUsageResponse> = {
      schema: dailyLinenUsageResponseSchema,
      params: { date },
    };

    try {
      return this.get<DailyLinenUsageResponse>('/linen-usage', config);
    } catch (error) {
      // Return null if not found (404)
      return null;
    }
  }

  /**
    * Создает или обновляет ежедневное использование белья (upsert)
    */
  async upsertDailyLinenUsage(data: PatchDailyLinenUsageRequest): Promise<DailyLinenUsageResponse> {
    const config: RequestConfig<DailyLinenUsageResponse> = {
      schema: dailyLinenUsageResponseSchema,
    };

    // Инвалидируем кэш после обновления
    const result = await this.patch<PatchDailyLinenUsageRequest, DailyLinenUsageResponse>(
      '/linen-usage',
      data,
      config
    );
    this.invalidateCache();
    return result;
  }

  /**
   * Синхронизирует daily_checkins с актуальным списком номеров из rooms
   * Добавляет новые номера и удаляет устаревшие снапшоты (Hard Delete)
   */
  async syncRooms(date: string): Promise<SyncCheckinsResponse> {
    const config: RequestConfig<SyncCheckinsResponse> = {
      schema: syncCheckinsResponseSchema,
    };

    const data: SyncCheckinsRequest = { date };

    // Инвалидируем кэш после синхронизации
    const result = await this.post<SyncCheckinsRequest, SyncCheckinsResponse>(
      '/checkins/sync',
      data,
      config
    );
    this.invalidateCache();
    return result;
  }

  async getCleaningScheduleByDate(date: string): Promise<CleaningScheduleEntryResponse[]> {
    const config: RequestConfig<CleaningScheduleEntryResponse[]> = {
      schema: cleaningScheduleListResponseSchema,
      params: { date },
    };

    return this.get<CleaningScheduleEntryResponse[]>('/cleaning-schedule', config);
  }

  async addCleaningEntry(data: { roomId: string; date: string; roomType: string; bedsCount?: number }): Promise<{ id: string }> {
    const config: RequestConfig<{ id: string }> = {
      schema: z.object({ id: z.string() }),
    };

    const result = await this.post<typeof data, { id: string }>(
      '/cleaning-schedule',
      data,
      config,
    );
    this.invalidateCache();
    return result;
  }

  async patchCleaningEntry(id: string, data: { isCleaned?: boolean; cleanerName?: string; comment?: string }): Promise<CleaningScheduleEntryResponse> {
    const config: RequestConfig<CleaningScheduleEntryResponse> = {
      schema: cleaningScheduleEntryResponseSchema,
    };

    const result = await this.patch<typeof data, CleaningScheduleEntryResponse>(
      `/cleaning-schedule/${id}`,
      data,
      config,
    );
    this.invalidateCache();
    return result;
  }

  async deleteCleaningEntry(id: string): Promise<void> {
    await this.delete(`/cleaning-schedule/${id}`);
    this.invalidateCache();
  }

  async syncCleaningFromCheckins(date: string): Promise<SyncCleaningScheduleResponse> {
    const config: RequestConfig<SyncCleaningScheduleResponse> = {
      schema: syncCleaningScheduleResponseSchema,
    };

    const result = await this.post<{ date: string }, SyncCleaningScheduleResponse>(
      '/cleaning-schedule/sync',
      { date },
      config,
    );
    this.invalidateCache();
    return result;
  }

  async getCleaningScheduleByMonth(month: number, year: number): Promise<CleaningScheduleMonthlyResponse> {
    const params: Record<string, any> = { month, year };
    const config: RequestConfig<CleaningScheduleMonthlyResponse> = {
      schema: cleaningScheduleMonthlyResponseSchema,
      params,
    };

    return this.get<CleaningScheduleMonthlyResponse>('/cleaning-schedule/monthly', config);
  }

  async syncCleaningRooms(month: number, year: number): Promise<{ added: number }> {
    const config: RequestConfig<{ added: number }> = {
      schema: z.object({ added: z.number() }),
    };

    const result = await this.post<{ month: number; year: number }, { added: number }>(
      '/cleaning-schedule/sync-rooms',
      { month, year },
      config,
    );
    this.invalidateCache();
    return result;
  }

  async addRoomToCleaningSchedule(data: { month: number; year: number; roomId: string; roomType: string; beds: number }): Promise<void> {
    await this.post<typeof data, void>('/cleaning-schedule/room', data);
    this.invalidateCache();
  }

  async removeRoomFromCleaningSchedule(month: number, year: number, roomId: string): Promise<void> {
    await this.delete('/cleaning-schedule/room', { params: { month, year, roomId } });
    this.invalidateCache();
  }

  async getPermanentRoomIds(): Promise<string[]> {
    const config: RequestConfig<string[]> = {
      schema: z.array(z.string()),
    };
    return this.get<string[]>('/cleaning-schedule/permanent-rooms', config);
  }
}

/**
 * Синглтон экземпляр OperationsRepository
 */
export const operationsRepository = new OperationsRepository();
