// src/server/features/rooms/rooms.service.ts
// Business logic for rooms management

import { db } from '../../shared/db/client';
import { rooms } from './db/rooms.table';
import { eq } from 'drizzle-orm';
import { AppError } from '../../shared/lib/errors';
import { createExtendedBaseService } from '../../shared/db/base.service';
import { emitRoomCreatedEvent, emitRoomUpdatedEvent, emitRoomDeletedEvent } from './rooms.events';
import type { CreateRoomInput, UpdateRoomInput, RoomResponse } from './rooms.schema';
import { z } from 'zod';

/**
 * Zod schema for Room Response DTO
 * Used for automatic validation and transformation in Base Service
 * Даты конвертируются в ISO строки в mapKeysToCamel (case.ts)
 */
const RoomResponseSchema = z.object({
  id: z.string(),
  type: z.enum(['апарт', 'номер']),
  hasHood: z.boolean(),
  beds: z.number(),
  armchair: z.boolean(),
  airCond: z.boolean(),
  isRepairing: z.boolean(),
  repairReason: z.string().nullable(),
  comment: z.string().nullable(),
  createdAt: z.string(),
  updatedAt: z.string(),
});

/**
 * Create extended base service with output schema and auto camelCase
 */
const roomsBaseService = createExtendedBaseService(
  rooms,
  {
    outputSchema: RoomResponseSchema,
    autoCamelCase: true,
  }
);

function parseRoomId(id: string): { letter: string; number: number } {
  const match = id.match(/^([A-Z])(\d+)$/);
  if (!match) {
    throw new AppError('Invalid room ID format', 400, 'INVALID_ROOM_ID');
  }
  return {
    letter: match[1],
    number: parseInt(match[2], 10),
  };
}

async function roomExists(id: string, excludeId?: string): Promise<boolean> {
  const existing = await db.select()
    .from(rooms)
    .where(eq(rooms.id, id))
    .limit(1);
  
  if (!existing[0]) {
    return false;
  }
  
  if (excludeId && existing[0].id === excludeId) {
    return false;
  }
  
  return true;
}

export async function createRoom(input: CreateRoomInput): Promise<RoomResponse> {
  const exists = await roomExists(input.id);
  if (exists) {
    throw new AppError('Room with this ID already exists', 409, 'ROOM_EXISTS');
  }

  const hasHood = input.type === 'апарт' ? input.hasHood : false;

  // Base Service automatically transforms snake_case -> camelCase and validates via Zod
  const response = await roomsBaseService.create({
    id: input.id,
    type: input.type,
    hasHood,
    beds: input.beds ?? 2,
    armchair: input.armchair ?? false,
    airCond: input.airCond ?? false,
    isRepairing: input.isRepairing ?? false,
    repairReason: input.repairReason ?? null,
    comment: input.comment ?? null,
  });

  await emitRoomCreatedEvent({
    roomId: response.id,
    room: response,
  });

  return response;
}

export async function getAllRooms(): Promise<RoomResponse[]> {
  // Base Service automatically transforms snake_case -> camelCase and validates via Zod
  const entries = await roomsBaseService.findAll();

  const sortedEntries = entries.sort((a, b) => {
    const aParsed = parseRoomId(a.id);
    const bParsed = parseRoomId(b.id);

    if (aParsed.letter !== bParsed.letter) {
      return aParsed.letter.localeCompare(bParsed.letter);
    }

    return aParsed.number - bParsed.number;
  });

  return sortedEntries;
}

export async function getRoomById(id: string): Promise<RoomResponse> {
  // Base Service automatically transforms snake_case -> camelCase and validates via Zod
  const entry = await roomsBaseService.findById(id);

  if (!entry) {
    throw new AppError('Room not found', 404, 'ROOM_NOT_FOUND');
  }

  return entry;
}

export async function updateRoom(id: string, input: UpdateRoomInput): Promise<RoomResponse> {
  // Base Service automatically transforms snake_case -> camelCase and validates via Zod
  const existing = await roomsBaseService.findById(id);

  if (!existing) {
    throw new AppError('Room not found', 404, 'ROOM_NOT_FOUND');
  }

  const existingRoom = existing;
  const newId = input.id;
  const oldId = id;

  if (newId && newId !== id) {
    const exists = await roomExists(newId, id);
    if (exists) {
      throw new AppError('Room with this ID already exists', 409, 'ROOM_EXISTS');
    }
  }

  const newType = input.type ?? existingRoom.type;

  let hasHood = input.hasHood;
  if (hasHood === undefined) {
    hasHood = existingRoom.hasHood;
  }

  if (newType !== 'апарт') {
    hasHood = false;
  }

  const newIsRepairing = input.isRepairing ?? existingRoom.isRepairing;

  let repairReason: string | null | undefined = input.repairReason;
  if (repairReason === undefined) {
    repairReason = existingRoom.repairReason;
  }

  const wasRepairing = Boolean(existingRoom.isRepairing);
  const isNowRepairing = Boolean(newIsRepairing);

  if (wasRepairing && !isNowRepairing) {
    repairReason = null;
  }

  // Use withTransaction for atomic operations
  const response = await roomsBaseService.withTransaction(async (tx) => {
    // Check for ID conflict in transaction if ID is changing
    if (newId && newId !== id) {
      const existingInTx = await tx.select()
        .from(rooms)
        .where(eq(rooms.id, newId))
        .limit(1);
      
      if (existingInTx[0]) {
        throw new AppError('Room with this ID already exists', 409, 'CONFLICT');
      }
    }

    // Single update operation
    await tx.update(rooms)
      .set({
        id: newId ?? id,
        type: newType,
        hasHood,
        beds: input.beds ?? existingRoom.beds,
        armchair: input.armchair ?? existingRoom.armchair,
        airCond: input.airCond ?? existingRoom.airCond,
        isRepairing: newIsRepairing,
        repairReason: repairReason ?? null,
        comment: input.comment !== undefined ? input.comment : existingRoom.comment,
        updatedAt: new Date(),
      })
      .where(eq(rooms.id, id));

    // Return the updated room using Base Service
    const updatedRoom = await roomsBaseService.findById(newId ?? id, tx);
    if (!updatedRoom) {
      throw new AppError('Failed to update room', 500, 'UPDATE_FAILED');
    }
    return updatedRoom;
  });

  await emitRoomUpdatedEvent({
    roomId: response.id,
    room: response,
    oldId: newId && newId !== oldId ? oldId : undefined,
  });

  return response;
}

export async function deleteRoom(id: string): Promise<void> {
  const existing = await roomsBaseService.findById(id);
  if (!existing) throw new AppError('Room not found', 404, 'ROOM_NOT_FOUND');

  // Используем прямой доступ к db для удаления (hard delete)
  await db.delete(rooms).where(eq(rooms.id, id));

  await emitRoomDeletedEvent({ roomId: id });
}
