// src/server/features/reference_books/local_it.service.ts
// Business logic for local IT, security, and other equipment management

import { randomUUID } from 'node:crypto';
import { localIT, localSecurity, otherEquipment } from './db/local_it.table';
import { db } from '../../shared/db/client';
import { eq, and, isNull } from 'drizzle-orm';
import { logger } from '../../shared/lib/logger';

/**
 * Access Port interface
 */
export interface AccessPort {
  serviceName: string;
  username?: string;
  password?: string;
}

/**
 * DTO для ответа API (Local IT)
 */
export interface LocalITDTO {
  id: string;
  description: string;
  manufacturer: string | null;
  model: string | null;
  networkAddress: string | null;
  accessPorts: AccessPort[] | null;
  username: string | null;
  password: string | null;
  location: string;
  createdAt: string;
  updatedAt: string;
  archivedAt: string | null;
}

/**
 * DTO для ответа API (Local Security)
 */
export interface LocalSecurityDTO {
  id: string;
  type: string;
  manufacturer: string | null;
  model: string | null;
  location: string;
  createdAt: string;
  updatedAt: string;
  archivedAt: string | null;
}

/**
 * DTO для ответа API (Other Equipment)
 */
export interface OtherEquipmentDTO {
  id: string;
  description: string;
  type: string | null;
  manufacturer: string | null;
  model: string | null;
  location: string;
  createdAt: string;
  updatedAt: string;
  archivedAt: string | null;
}

/**
 * Приводит данные Local IT к DTO формату
 */
function mapToLocalITDTO(row: typeof localIT.$inferSelect): LocalITDTO {
  return {
    id: row.id,
    description: row.description,
    manufacturer: row.manufacturer,
    model: row.model,
    networkAddress: row.networkAddress,
    accessPorts: row.accessPorts ? JSON.parse(row.accessPorts as string) : null,
    username: row.username,
    password: row.password,
    location: row.location,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
    archivedAt: row.archivedAt?.toISOString() || null,
  };
}

/**
 * Приводит данные Local Security к DTO формату
 */
function mapToLocalSecurityDTO(row: typeof localSecurity.$inferSelect): LocalSecurityDTO {
  return {
    id: row.id,
    type: row.type,
    manufacturer: row.manufacturer,
    model: row.model,
    location: row.location,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
    archivedAt: row.archivedAt?.toISOString() || null,
  };
}

/**
 * Приводит данные Other Equipment к DTO формату
 */
function mapToOtherEquipmentDTO(row: typeof otherEquipment.$inferSelect): OtherEquipmentDTO {
  return {
    id: row.id,
    description: row.description,
    type: row.type,
    manufacturer: row.manufacturer,
    model: row.model,
    location: row.location,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
    archivedAt: row.archivedAt?.toISOString() || null,
  };
}

/**
 * Сервис управления Local IT оборудованием
 */
export const localITService = {
  /**
   * Получить все записи Local IT
   */
  async findAll(): Promise<LocalITDTO[]> {
    const items = await db
      .select()
      .from(localIT)
      .where(isNull(localIT.archivedAt))
      .orderBy(localIT.createdAt);
    return items.map(mapToLocalITDTO);
  },

  /**
   * Получить запись Local IT по ID
   */
  async findById(id: string): Promise<LocalITDTO | null> {
    const items = await db
      .select()
      .from(localIT)
      .where(and(
        eq(localIT.id, id),
        isNull(localIT.archivedAt)
      ))
      .limit(1);
    
    return items.length > 0 ? mapToLocalITDTO(items[0]) : null;
  },

  /**
   * Создать новую запись Local IT
   */
  async create(data: Omit<typeof localIT.$inferInsert, 'id' | 'createdAt' | 'updatedAt' | 'archivedAt'>): Promise<LocalITDTO> {
    logger.info(`[LocalIT] Creating entry: ${data.description}`);
    
    const now = new Date();
    const id = randomUUID();
    
    await db
      .insert(localIT)
      .values({
        ...data,
        accessPorts: data.accessPorts ? JSON.stringify(data.accessPorts) : null,
        id,
        createdAt: now,
        updatedAt: now,
        archivedAt: null,
      });
    
    const created = await db
      .select()
      .from(localIT)
      .where(eq(localIT.id, id))
      .limit(1);
    
    return mapToLocalITDTO(created[0]);
  },

  /**
   * Обновить запись Local IT
   */
  async update(id: string, data: Partial<Omit<typeof localIT.$inferInsert, 'id' | 'createdAt' | 'updatedAt' | 'archivedAt'>>): Promise<LocalITDTO | null> {
    logger.info(`[LocalIT] Updating entry: ${id}`);
    
    const updateData: any = { ...data };
    if (data.accessPorts !== undefined) {
      updateData.accessPorts = data.accessPorts ? JSON.stringify(data.accessPorts) : null;
    }
    
    await db
      .update(localIT)
      .set({
        ...updateData,
        updatedAt: new Date(),
      })
      .where(eq(localIT.id, id));
    
    const updated = await db
      .select()
      .from(localIT)
      .where(eq(localIT.id, id))
      .limit(1);
    
    return updated.length > 0 ? mapToLocalITDTO(updated[0]) : null;
  },

  /**
   * Архивировать запись Local IT
   */
  async softDelete(id: string): Promise<void> {
    logger.info(`[LocalIT] Archiving entry: ${id}`);
    
    await db
      .update(localIT)
      .set({
        archivedAt: new Date(),
        updatedAt: new Date(),
      })
      .where(eq(localIT.id, id));
  },
};

/**
 * Сервис управления Local Security оборудованием
 */
export const localSecurityService = {
  /**
   * Получить все записи Local Security
   */
  async findAll(): Promise<LocalSecurityDTO[]> {
    const items = await db
      .select()
      .from(localSecurity)
      .where(isNull(localSecurity.archivedAt))
      .orderBy(localSecurity.createdAt);
    return items.map(mapToLocalSecurityDTO);
  },

  /**
   * Получить запись Local Security по ID
   */
  async findById(id: string): Promise<LocalSecurityDTO | null> {
    const items = await db
      .select()
      .from(localSecurity)
      .where(and(
        eq(localSecurity.id, id),
        isNull(localSecurity.archivedAt)
      ))
      .limit(1);
    
    return items.length > 0 ? mapToLocalSecurityDTO(items[0]) : null;
  },

  /**
   * Создать новую запись Local Security
   */
  async create(data: Omit<typeof localSecurity.$inferInsert, 'id' | 'createdAt' | 'updatedAt' | 'archivedAt'>): Promise<LocalSecurityDTO> {
    logger.info(`[LocalSecurity] Creating entry: ${data.type}`);
    
    const now = new Date();
    const id = randomUUID();
    
    await db
      .insert(localSecurity)
      .values({
        ...data,
        id,
        createdAt: now,
        updatedAt: now,
        archivedAt: null,
      });
    
    const created = await db
      .select()
      .from(localSecurity)
      .where(eq(localSecurity.id, id))
      .limit(1);
    
    return mapToLocalSecurityDTO(created[0]);
  },

  /**
   * Обновить запись Local Security
   */
  async update(id: string, data: Partial<Omit<typeof localSecurity.$inferInsert, 'id' | 'createdAt' | 'updatedAt' | 'archivedAt'>>): Promise<LocalSecurityDTO | null> {
    logger.info(`[LocalSecurity] Updating entry: ${id}`);
    
    await db
      .update(localSecurity)
      .set({
        ...data,
        updatedAt: new Date(),
      })
      .where(eq(localSecurity.id, id));
    
    const updated = await db
      .select()
      .from(localSecurity)
      .where(eq(localSecurity.id, id))
      .limit(1);
    
    return updated.length > 0 ? mapToLocalSecurityDTO(updated[0]) : null;
  },

  /**
   * Архивировать запись Local Security
   */
  async softDelete(id: string): Promise<void> {
    logger.info(`[LocalSecurity] Archiving entry: ${id}`);
    
    await db
      .update(localSecurity)
      .set({
        archivedAt: new Date(),
        updatedAt: new Date(),
      })
      .where(eq(localSecurity.id, id));
  },
};

/**
 * Сервис управления Other Equipment
 */
export const otherEquipmentService = {
  /**
   * Получить все записи Other Equipment
   */
  async findAll(): Promise<OtherEquipmentDTO[]> {
    const items = await db
      .select()
      .from(otherEquipment)
      .where(isNull(otherEquipment.archivedAt))
      .orderBy(otherEquipment.createdAt);
    return items.map(mapToOtherEquipmentDTO);
  },

  /**
   * Получить запись Other Equipment по ID
   */
  async findById(id: string): Promise<OtherEquipmentDTO | null> {
    const items = await db
      .select()
      .from(otherEquipment)
      .where(and(
        eq(otherEquipment.id, id),
        isNull(otherEquipment.archivedAt)
      ))
      .limit(1);
    
    return items.length > 0 ? mapToOtherEquipmentDTO(items[0]) : null;
  },

  /**
   * Создать новую запись Other Equipment
   */
  async create(data: Omit<typeof otherEquipment.$inferInsert, 'id' | 'createdAt' | 'updatedAt' | 'archivedAt'>): Promise<OtherEquipmentDTO> {
    logger.info(`[OtherEquipment] Creating entry: ${data.description}`);
    
    const now = new Date();
    const id = randomUUID();
    
    await db
      .insert(otherEquipment)
      .values({
        ...data,
        id,
        createdAt: now,
        updatedAt: now,
        archivedAt: null,
      });
    
    const created = await db
      .select()
      .from(otherEquipment)
      .where(eq(otherEquipment.id, id))
      .limit(1);
    
    return mapToOtherEquipmentDTO(created[0]);
  },

  /**
   * Обновить запись Other Equipment
   */
  async update(id: string, data: Partial<Omit<typeof otherEquipment.$inferInsert, 'id' | 'createdAt' | 'updatedAt' | 'archivedAt'>>): Promise<OtherEquipmentDTO | null> {
    logger.info(`[OtherEquipment] Updating entry: ${id}`);
    
    await db
      .update(otherEquipment)
      .set({
        ...data,
        updatedAt: new Date(),
      })
      .where(eq(otherEquipment.id, id));
    
    const updated = await db
      .select()
      .from(otherEquipment)
      .where(eq(otherEquipment.id, id))
      .limit(1);
    
    return updated.length > 0 ? mapToOtherEquipmentDTO(updated[0]) : null;
  },

  /**
   * Архивировать запись Other Equipment
   */
  async softDelete(id: string): Promise<void> {
    logger.info(`[OtherEquipment] Archiving entry: ${id}`);
    
    await db
      .update(otherEquipment)
      .set({
        archivedAt: new Date(),
        updatedAt: new Date(),
      })
      .where(eq(otherEquipment.id, id));
  },
};
