const { all, audit, json, now, one, parseJson, run } = require('../db');

const TYPES = ['employee', 'vendor', 'service_provider', 'contractor', 'work_location', 'other'];
const CREDIT_TERMS = ['na', 'net_30', 'net_60', 'net_90', 'net_180'];

const COLUMNS = [
  'type', 'first_name', 'last_name', 'organization', 'email', 'phone',
  'address_line1', 'address_line2', 'city', 'state_region', 'postal_code', 'country',
  'notes', 'status', 'employee_id', 'tax_id', 'department', 'work_location',
  'manager_first_name', 'manager_last_name', 'manager_email', 'start_date',
  'rating', 'credit_term', 'source', 'workday_worker_id'
];

function contactNotes(contactId) {
  return all(
    `SELECT n.id, n.body, n.created_at, u.name AS author
     FROM contact_notes n LEFT JOIN users u ON u.id = n.created_by
     WHERE n.contact_id = ? ORDER BY n.id DESC`,
    [contactId]
  );
}

function displayName(row) {
  const person = [row.first_name, row.last_name].filter(Boolean).join(' ').trim();
  return row.organization || person || row.email || `Contact ${row.id}`;
}

function fromRow(row, withNotes = false) {
  if (!row) return null;
  const contact = { ...row, display_name: displayName(row) };
  if (withNotes) contact.contact_notes = contactNotes(row.id);
  return contact;
}

function normalize(body) {
  const input = {};
  for (const column of COLUMNS) {
    if (Object.prototype.hasOwnProperty.call(body, column)) input[column] = body[column];
  }
  input.type = TYPES.includes(input.type) ? input.type : 'other';
  input.status = input.status || 'active';
  input.source = input.source || 'manual';
  if (input.credit_term && !CREDIT_TERMS.includes(input.credit_term)) input.credit_term = 'na';
  if (input.rating != null && input.rating !== '') input.rating = Math.max(0, Math.min(5, Number(input.rating)));
  else input.rating = null;
  if (input.workday_worker_id === '') input.workday_worker_id = null;
  return input;
}

function listContacts(query = {}, companyId) {
  return all(
    `SELECT * FROM contacts
     WHERE (? IS NULL OR entity_id = ?)
       AND (? IS NULL OR type = ?)
       AND (? IS NULL OR status = ?)
       AND (? IS NULL OR (
         first_name LIKE '%' || ? || '%' OR last_name LIKE '%' || ? || '%' OR
         organization LIKE '%' || ? || '%' OR email LIKE '%' || ? || '%' OR
         employee_id LIKE '%' || ? || '%'
       ))
     ORDER BY type, organization, last_name, first_name`,
    [
      companyId || null, companyId || null,
      query.type || null, query.type || null,
      query.status || null, query.status || null,
      query.search || null, query.search || null, query.search || null,
      query.search || null, query.search || null, query.search || null
    ]
  ).map((row) => fromRow(row));
}

function getContact(id, companyId) {
  return fromRow(one('SELECT * FROM contacts WHERE id = ? AND (? IS NULL OR entity_id = ?)', [id, companyId || null, companyId || null]), true);
}

function createContact(body, userId, companyId) {
  const input = normalize(body);
  const ts = now();
  const cols = COLUMNS.filter((c) => input[c] !== undefined);
  const result = run(
    `INSERT INTO contacts (entity_id, ${cols.join(', ')}, created_by, created_at, updated_at)
     VALUES (?, ${cols.map(() => '?').join(', ')}, ?, ?, ?)`,
    [companyId || null, ...cols.map((c) => input[c]), userId, ts, ts]
  );
  audit(userId, 'create', 'contact', result.lastInsertRowid, null, { type: input.type, name: displayName(input) });
  return getContact(result.lastInsertRowid, companyId);
}

function updateContact(id, body, userId, companyId) {
  const before = one('SELECT * FROM contacts WHERE id = ?', [id]);
  if (!before || (companyId && before.entity_id !== companyId)) return null;
  const input = normalize({ ...before, ...body });
  run(
    `UPDATE contacts SET ${COLUMNS.map((c) => `${c} = ?`).join(', ')}, updated_at = ? WHERE id = ?`,
    [...COLUMNS.map((c) => input[c] ?? null), now(), id]
  );
  audit(userId, 'update', 'contact', id, before, body);
  return getContact(id, companyId);
}

function deleteContact(id, userId, companyId) {
  const before = one('SELECT * FROM contacts WHERE id = ?', [id]);
  if (!before || (companyId && before.entity_id !== companyId)) return false;
  run('DELETE FROM contacts WHERE id = ?', [id]);
  audit(userId, 'delete', 'contact', id, before, null);
  return true;
}

function addNote(contactId, bodyText, userId, companyId) {
  if (!one('SELECT id FROM contacts WHERE id = ? AND (? IS NULL OR entity_id = ?)', [contactId, companyId || null, companyId || null])) return null;
  const result = run('INSERT INTO contact_notes (contact_id, body, created_by, created_at) VALUES (?, ?, ?, ?)', [contactId, bodyText, userId, now()]);
  audit(userId, 'create', 'contact_note', result.lastInsertRowid, null, { contact_id: contactId });
  return one('SELECT n.id, n.body, n.created_at, u.name AS author FROM contact_notes n LEFT JOIN users u ON u.id = n.created_by WHERE n.id = ?', [result.lastInsertRowid]);
}

function deleteNote(contactId, noteId, userId, companyId) {
  if (companyId && !one('SELECT id FROM contacts WHERE id = ? AND entity_id = ?', [contactId, companyId])) return false;
  const result = run('DELETE FROM contact_notes WHERE id = ? AND contact_id = ?', [noteId, contactId]);
  if (!result.changes) return false;
  audit(userId, 'delete', 'contact_note', noteId, null, { contact_id: contactId });
  return true;
}

// Upsert an employee contact from a normalized Workday worker record.
function upsertWorkdayContact(worker) {
  const ts = now();
  const existing = worker.workday_worker_id
    ? one('SELECT id FROM contacts WHERE workday_worker_id = ?', [worker.workday_worker_id])
    : (worker.email ? one('SELECT id FROM contacts WHERE type = ? AND lower(email) = lower(?)', ['employee', worker.email]) : null);

  const fields = {
    type: 'employee',
    first_name: worker.first_name || null,
    last_name: worker.last_name || null,
    organization: null,
    email: worker.email || null,
    department: worker.department || null,
    work_location: worker.location || null,
    employee_id: worker.employee_number || null,
    manager_first_name: worker.manager_first_name || null,
    manager_last_name: worker.manager_last_name || null,
    manager_email: worker.manager_email || null,
    start_date: worker.start_date || null,
    status: worker.status || 'active',
    source: 'workday',
    workday_worker_id: worker.workday_worker_id || null,
    source_payload: json(worker.source_payload || worker)
  };

  if (existing) {
    const cols = Object.keys(fields);
    run(
      `UPDATE contacts SET ${cols.map((c) => `${c} = COALESCE(?, ${c})`).join(', ')}, last_synced_at = ?, updated_at = ? WHERE id = ?`,
      [...cols.map((c) => fields[c]), ts, ts, existing.id]
    );
    return existing.id;
  }
  // Workday sync is global (no request company context); new workers land in the default company.
  const defaultCompany = one("SELECT id FROM entities WHERE status = 'active' ORDER BY id LIMIT 1");
  const cols = Object.keys(fields);
  const result = run(
    `INSERT INTO contacts (entity_id, ${cols.join(', ')}, last_synced_at, created_at, updated_at) VALUES (?, ${cols.map(() => '?').join(', ')}, ?, ?, ?)`,
    [defaultCompany ? defaultCompany.id : null, ...cols.map((c) => fields[c]), ts, ts, ts]
  );
  return result.lastInsertRowid;
}

module.exports = {
  CREDIT_TERMS,
  TYPES,
  addNote,
  createContact,
  deleteContact,
  deleteNote,
  getContact,
  listContacts,
  parsePayload: (value) => parseJson(value, null),
  updateContact,
  upsertWorkdayContact
};
