import { findAll, findById, insert, update, softDelete } from '../repositories/client.repo.js';
import { findByClientId, insert as insertSeal, softDelete as softDeleteSeal } from '../repositories/seal.repo.js';
import { hasOrdersForClient } from '../repositories/order.repo.js';
import { createWithId } from '../utils/id.js';
import { NotFoundError, ConflictError } from '../utils/errors.js';
import { emitter } from '../events/emitter.js';
import type { Client, PaginatedResult } from '../types/index.js';

export async function list(search?: string, page = 1, limit = 20): Promise<PaginatedResult<Client>> {
  const { rows, total } = await findAll(search, page, limit);
  const clients: Client[] = [];
  for (const row of rows) {
    const seals = await findByClientId(row.id);
    clients.push({ ...row, seals });
  }
  return { data: clients, total, page, limit };
}

export async function get(id: string): Promise<Client> {
  const client = await findById(id);
  if (!client) throw new NotFoundError('Client not found');
  const seals = await findByClientId(id);
  return { ...client, seals };
}

export async function create(data: Partial<Client>, userId: string): Promise<Client> {
  const { id } = await createWithId('clients', 'CLI', (id) => insert(data, id, userId));

  if (data.seals && Array.isArray(data.seals)) {
    for (const seal of data.seals) {
      if (seal.name && seal.name.trim()) {
        await createWithId('seals', 'SEAL', (sealId) => insertSeal(id, seal.name.trim(), sealId, userId));
      }
    }
  }

  const client = await findById(id);
  if (!client) throw new NotFoundError('Client not found');
  emitter.emit('activity.create', { entity_type: 'client', entity_id: id, action: 'created', user_id: userId });
  const seals = await findByClientId(id);
  return { ...client, seals };
}

export async function updateClient(id: string, data: Partial<Client>, userId: string): Promise<Client> {
  const existing = await findById(id);
  if (!existing) throw new NotFoundError('Client not found');
  await update(id, data, userId);
  
  if (data.seals && Array.isArray(data.seals)) {
    const existingSeals = await findByClientId(id);
    const incomingNames = new Set(data.seals.map((s) => s.name.trim().toLowerCase()));
    
    // Delete seals that are not in the incoming list
    for (const ext of existingSeals) {
      if (!incomingNames.has(ext.name.trim().toLowerCase())) {
        await softDeleteSeal(ext.id, userId);
      }
    }
    
    // Add new seals that do not exist yet in the database
    const existingNames = new Set(existingSeals.map((s) => s.name.trim().toLowerCase()));
    for (const seal of data.seals) {
      if (seal.name && seal.name.trim() && !existingNames.has(seal.name.trim().toLowerCase())) {
        await createWithId('seals', 'SEAL', (sealId) => insertSeal(id, seal.name.trim(), sealId, userId));
      }
    }
  }

  emitter.emit('activity.create', { entity_type: 'client', entity_id: id, action: 'updated', user_id: userId });
  return get(id);
}

export async function remove(id: string, userId: string): Promise<void> {
  const existing = await findById(id);
  if (!existing) throw new NotFoundError('Client not found');
  if (await hasOrdersForClient(id)) {
    throw new ConflictError('This client has existing orders and cannot be deleted');
  }
  await softDelete(id, userId);
  emitter.emit('activity.create', { entity_type: 'client', entity_id: id, action: 'deleted', user_id: userId });
}

export async function addSeal(clientId: string, name: string, userId: string): Promise<{ id: string; client_id: string; name: string }> {
  const client = await findById(clientId);
  if (!client) throw new NotFoundError('Client not found');
  const { id } = await createWithId('seals', 'SEAL', (id) => insertSeal(clientId, name, id, userId));
  return { id, client_id: clientId, name };
}

export async function removeSeal(clientId: string, sealId: string, userId: string): Promise<void> {
  await softDeleteSeal(sealId, userId);
  emitter.emit('activity.create', { entity_type: 'seal', entity_id: sealId, action: 'deleted', user_id: userId });
}
