import { db } from '../config/database.js';
import { notes, companies, projects, todos, contacts, users } from '../db/schema.js';
import { eq, desc, ilike, or, and } from 'drizzle-orm';
import { NotFoundError } from '../utils/errors.js';
import { logNoteCreated, logNoteUpdated, logNoteDeleted } from './audit.service.js';

/**
 * Get all notes
 * Optionally filter by search, company, project, todo, or contact
 * Returns notes with creator info
 */
export const getAllNotes = async (filters = {}) => {
  const { searchTerm, companyId, projectId, todoId, contactId } = filters;

  let query = db
    .select({
      id: notes.id,
      title: notes.title,
      content: notes.content,
      companyId: notes.companyId,
      projectId: notes.projectId,
      todoId: notes.todoId,
      contactId: notes.contactId,
      dueDate: notes.dueDate,
      createdBy: notes.createdBy,
      createdAt: notes.createdAt,
      updatedAt: notes.updatedAt,
      creator: {
        id: users.id,
        username: users.username,
      },
    })
    .from(notes)
    .leftJoin(users, eq(notes.createdBy, users.id));

  const conditions = [];

  if (searchTerm) {
    conditions.push(
      or(
        ilike(notes.title, `%${searchTerm}%`),
        ilike(notes.content, `%${searchTerm}%`)
      )
    );
  }

  if (companyId) {
    conditions.push(eq(notes.companyId, companyId));
  }

  if (projectId) {
    conditions.push(eq(notes.projectId, projectId));
  }

  if (todoId) {
    conditions.push(eq(notes.todoId, todoId));
  }

  if (contactId) {
    conditions.push(eq(notes.contactId, contactId));
  }

  if (conditions.length > 0) {
    query = query.where(and(...conditions));
  }

  return await query.orderBy(desc(notes.createdAt));
};

/**
 * Get note by ID
 */
export const getNoteById = async (noteId) => {
  const [note] = await db
    .select()
    .from(notes)
    .where(eq(notes.id, noteId))
    .limit(1);

  if (!note) {
    throw new NotFoundError('Poznámka nenájdená');
  }

  return note;
};

/**
 * Create new note
 */
export const createNote = async (userId, data, auditContext = null) => {
  const { title, content, companyId, projectId, todoId, contactId, dueDate } = data;

  // Verify company exists if provided
  if (companyId) {
    const [company] = await db
      .select()
      .from(companies)
      .where(eq(companies.id, companyId))
      .limit(1);

    if (!company) {
      throw new NotFoundError('Firma nenájdená');
    }
  }

  // Verify project exists if provided
  if (projectId) {
    const [project] = await db
      .select()
      .from(projects)
      .where(eq(projects.id, projectId))
      .limit(1);

    if (!project) {
      throw new NotFoundError('Projekt nenájdený');
    }
  }

  // Verify todo exists if provided
  if (todoId) {
    const [todo] = await db
      .select()
      .from(todos)
      .where(eq(todos.id, todoId))
      .limit(1);

    if (!todo) {
      throw new NotFoundError('Todo nenájdené');
    }
  }

  // Verify contact exists if provided
  if (contactId) {
    const [contact] = await db
      .select()
      .from(contacts)
      .where(eq(contacts.id, contactId))
      .limit(1);

    if (!contact) {
      throw new NotFoundError('Kontakt nenájdený');
    }
  }

  const [newNote] = await db
    .insert(notes)
    .values({
      title: title || null,
      content,
      companyId: companyId || null,
      projectId: projectId || null,
      todoId: todoId || null,
      contactId: contactId || null,
      dueDate: dueDate ? new Date(dueDate) : null,
      createdBy: userId,
    })
    .returning();

  if (auditContext) {
    await logNoteCreated(auditContext.userId, newNote.id, newNote.content, auditContext.ipAddress, auditContext.userAgent);
  }

  return newNote;
};

/**
 * Update note
 */
export const updateNote = async (noteId, data, auditContext = null) => {
  const note = await getNoteById(noteId);

  const { title, content, companyId, projectId, todoId, contactId, dueDate } = data;

  // Verify company exists if being changed
  if (companyId !== undefined && companyId !== null && companyId !== note.companyId) {
    const [company] = await db
      .select()
      .from(companies)
      .where(eq(companies.id, companyId))
      .limit(1);

    if (!company) {
      throw new NotFoundError('Firma nenájdená');
    }
  }

  // Verify project exists if being changed
  if (projectId !== undefined && projectId !== null && projectId !== note.projectId) {
    const [project] = await db
      .select()
      .from(projects)
      .where(eq(projects.id, projectId))
      .limit(1);

    if (!project) {
      throw new NotFoundError('Projekt nenájdený');
    }
  }

  // Verify todo exists if being changed
  if (todoId !== undefined && todoId !== null && todoId !== note.todoId) {
    const [todo] = await db
      .select()
      .from(todos)
      .where(eq(todos.id, todoId))
      .limit(1);

    if (!todo) {
      throw new NotFoundError('Todo nenájdené');
    }
  }

  // Verify contact exists if being changed
  if (contactId !== undefined && contactId !== null && contactId !== note.contactId) {
    const [contact] = await db
      .select()
      .from(contacts)
      .where(eq(contacts.id, contactId))
      .limit(1);

    if (!contact) {
      throw new NotFoundError('Kontakt nenájdený');
    }
  }

  // Spracovanie dueDate - konverzia na Date objekt alebo null
  let parsedDueDate = note.dueDate;
  if (dueDate !== undefined) {
    parsedDueDate = dueDate ? new Date(dueDate) : null;
  }

  const [updated] = await db
    .update(notes)
    .set({
      title: title !== undefined ? title : note.title,
      content: content !== undefined ? content : note.content,
      companyId: companyId !== undefined ? companyId : note.companyId,
      projectId: projectId !== undefined ? projectId : note.projectId,
      todoId: todoId !== undefined ? todoId : note.todoId,
      contactId: contactId !== undefined ? contactId : note.contactId,
      dueDate: parsedDueDate,
      updatedAt: new Date(),
    })
    .where(eq(notes.id, noteId))
    .returning();

  if (auditContext) {
    await logNoteUpdated(auditContext.userId, noteId, note.content, updated.content, auditContext.ipAddress, auditContext.userAgent);
  }

  return updated;
};

/**
 * Delete note
 */
export const deleteNote = async (noteId, auditContext = null) => {
  const note = await getNoteById(noteId); // Check if exists

  await db.delete(notes).where(eq(notes.id, noteId));

  if (auditContext) {
    await logNoteDeleted(auditContext.userId, noteId, note.content, auditContext.ipAddress, auditContext.userAgent);
  }

  return { success: true, message: 'Poznámka bola odstránená' };
};

/**
 * Get notes by company ID
 * Returns notes with creator info
 */
export const getNotesByCompanyId = async (companyId) => {
  return await db
    .select({
      id: notes.id,
      title: notes.title,
      content: notes.content,
      companyId: notes.companyId,
      projectId: notes.projectId,
      todoId: notes.todoId,
      contactId: notes.contactId,
      dueDate: notes.dueDate,
      createdBy: notes.createdBy,
      createdAt: notes.createdAt,
      updatedAt: notes.updatedAt,
      creator: {
        id: users.id,
        username: users.username,
      },
    })
    .from(notes)
    .leftJoin(users, eq(notes.createdBy, users.id))
    .where(eq(notes.companyId, companyId))
    .orderBy(desc(notes.createdAt));
};

/**
 * Get notes by project ID
 * Returns notes with creator info
 */
export const getNotesByProjectId = async (projectId) => {
  return await db
    .select({
      id: notes.id,
      title: notes.title,
      content: notes.content,
      companyId: notes.companyId,
      projectId: notes.projectId,
      todoId: notes.todoId,
      contactId: notes.contactId,
      dueDate: notes.dueDate,
      createdBy: notes.createdBy,
      createdAt: notes.createdAt,
      updatedAt: notes.updatedAt,
      creator: {
        id: users.id,
        username: users.username,
      },
    })
    .from(notes)
    .leftJoin(users, eq(notes.createdBy, users.id))
    .where(eq(notes.projectId, projectId))
    .orderBy(desc(notes.createdAt));
};

/**
 * Get notes by todo ID
 */
export const getNotesByTodoId = async (todoId) => {
  return await db
    .select()
    .from(notes)
    .where(eq(notes.todoId, todoId))
    .orderBy(desc(notes.createdAt));
};

/**
 * Get notes by contact ID
 */
export const getNotesByContactId = async (contactId) => {
  return await db
    .select()
    .from(notes)
    .where(eq(notes.contactId, contactId))
    .orderBy(desc(notes.createdAt));
};

/**
 * Search notes with company and project info
 * Returns notes matching searchTerm with linked company/project names
 */
export const searchNotes = async (searchTerm) => {
  if (!searchTerm || searchTerm.trim().length < 2) {
    return [];
  }

  const results = await db
    .select({
      id: notes.id,
      content: notes.content,
      companyId: notes.companyId,
      projectId: notes.projectId,
      companyName: companies.name,
      projectName: projects.name,
      createdAt: notes.createdAt,
    })
    .from(notes)
    .leftJoin(companies, eq(notes.companyId, companies.id))
    .leftJoin(projects, eq(notes.projectId, projects.id))
    .where(ilike(notes.content, `%${searchTerm}%`))
    .orderBy(desc(notes.createdAt))
    .limit(20);

  return results;
};
