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

function employeeFromRow(row) {
  if (!row) return null;
  return {
    ...row,
    source_payload: parseJson(row.source_payload, null)
  };
}

function assignmentFromRow(row) {
  if (!row) return null;
  return {
    ...row,
    active: !row.released_at
  };
}

function listEmployees(query = {}, companyId) {
  return all(`
    SELECT *
    FROM employees
    WHERE (? IS NULL OR entity_id = ?)
      AND (? IS NULL OR status = ?)
      AND (? IS NULL OR name LIKE '%' || ? || '%' OR email LIKE '%' || ? || '%' OR employee_number LIKE '%' || ? || '%')
    ORDER BY name
    LIMIT 500
  `, [
    companyId || null, companyId || null,
    query.status || null,
    query.status || null,
    query.search || null,
    query.search || null,
    query.search || null,
    query.search || null
  ]).map(employeeFromRow);
}

// Manual creates use the active company; Workday sync (no companyId) lands in the default company.
function upsertEmployee(employee, companyId) {
  const timestamp = now();
  const entityId = companyId || (one("SELECT id FROM entities WHERE status = 'active' ORDER BY id LIMIT 1")?.id) || null;
  const workdayId = employee.workday_worker_id || employee.workdayWorkerId || employee.workerId || null;
  const existing = workdayId
    ? one('SELECT id FROM employees WHERE workday_worker_id = ?', [workdayId])
    : one('SELECT id FROM employees WHERE entity_id = ? AND (employee_number = ? OR lower(email) = lower(?))', [entityId, employee.employee_number || '', employee.email || '']);

  const values = [
    workdayId,
    employee.employee_number || employee.employeeNumber || employee.workerNumber || null,
    employee.name || employee.fullName || [employee.firstName, employee.lastName].filter(Boolean).join(' ') || 'Unnamed employee',
    employee.email || employee.workEmail || null,
    employee.department || employee.organization || null,
    employee.location || employee.workLocation || null,
    employee.manager_name || employee.managerName || null,
    employee.status || 'active',
    employee.source || 'manual',
    json(employee.source_payload || employee),
    employee.last_synced_at || null,
    timestamp
  ];

  if (existing) {
    run(
      `UPDATE employees SET
        workday_worker_id = COALESCE(?, workday_worker_id),
        employee_number = ?,
        name = ?,
        email = ?,
        department = ?,
        location = ?,
        manager_name = ?,
        status = ?,
        source = ?,
        source_payload = ?,
        last_synced_at = COALESCE(?, last_synced_at),
        updated_at = ?
      WHERE id = ?`,
      [...values, existing.id]
    );
    return one('SELECT * FROM employees WHERE id = ?', [existing.id]);
  }

  const result = run(
    `INSERT INTO employees (
      entity_id, workday_worker_id, employee_number, name, email, department, location, manager_name,
      status, source, source_payload, last_synced_at, created_at, updated_at
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
    [entityId, ...values, timestamp]
  );
  return one('SELECT * FROM employees WHERE id = ?', [result.lastInsertRowid]);
}

function assignmentRows(query = {}, companyId) {
  return all(`
    SELECT aa.*, a.asset_id AS asset_code, a.description AS asset_description,
      e.name AS employee_name, e.email AS employee_email, e.employee_number
    FROM asset_assignments aa
    JOIN assets a ON a.id = aa.asset_id
    LEFT JOIN employees e ON e.id = aa.employee_id
    WHERE (? IS NULL OR a.entity_id = ?)
      AND (? IS NULL OR aa.asset_id = ?)
      AND (? IS NULL OR aa.employee_id = ?)
      AND (? IS NULL OR aa.released_at IS NULL)
    ORDER BY aa.assigned_at DESC, aa.id DESC
    LIMIT 500
  `, [
    companyId || null, companyId || null,
    query.asset_id || null,
    query.asset_id || null,
    query.employee_id || null,
    query.employee_id || null,
    query.active ? 1 : null
  ]).map(assignmentFromRow);
}

function assignAsset(body, userId, companyId) {
  const timestamp = now();
  // The asset must belong to the active company so assignments can't cross companies.
  const asset = one('SELECT * FROM assets WHERE id = ? AND (? IS NULL OR entity_id = ?)', [body.asset_id, companyId || null, companyId || null]);
  if (!asset) throw Object.assign(new Error('Asset not found'), { status: 404 });

  // Only an employee in the same company may be assigned (foreign ids resolve to no employee).
  const employee = body.employee_id ? one('SELECT * FROM employees WHERE id = ? AND (? IS NULL OR entity_id = ?)', [body.employee_id, companyId || null, companyId || null]) : null;
  const department = body.department ?? employee?.department ?? asset.department ?? null;
  const location = body.location ?? employee?.location ?? asset.location ?? null;

  return tx(() => {
    run(
      `UPDATE asset_assignments
       SET released_at = ?, status = 'released', release_reason = 'Reassigned', updated_at = ?
       WHERE asset_id = ? AND released_at IS NULL`,
      [timestamp, timestamp, body.asset_id]
    );

    const result = run(
      `INSERT INTO asset_assignments (
        asset_id, employee_id, department, location, status, assigned_at, assigned_by, notes, created_at, updated_at
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        body.asset_id,
        employee ? employee.id : null,
        department,
        location,
        'assigned',
        body.assigned_at || timestamp,
        userId,
        body.notes || null,
        timestamp,
        timestamp
      ]
    );

    run(
      `UPDATE assets SET
        custodian = ?,
        department = ?,
        location = ?,
        status = 'in_service',
        updated_at = ?
      WHERE id = ?`,
      [employee?.name || asset.custodian || null, department, location, timestamp, body.asset_id]
    );

    const assignment = assignmentRows({ asset_id: body.asset_id, active: true }, companyId)[0];
    audit(userId, 'assign', 'asset', body.asset_id, null, assignment);
    return assignment || one('SELECT * FROM asset_assignments WHERE id = ?', [result.lastInsertRowid]);
  });
}

function releaseAssignment(id, body, userId, companyId) {
  const before = one(
    'SELECT aa.*, a.entity_id AS asset_entity_id FROM asset_assignments aa JOIN assets a ON a.id = aa.asset_id WHERE aa.id = ?',
    [id]
  );
  if (!before || (companyId && before.asset_entity_id !== companyId)) return null;
  const timestamp = now();
  run(
    `UPDATE asset_assignments
     SET released_at = ?, status = 'released', release_reason = ?, notes = COALESCE(?, notes), updated_at = ?
     WHERE id = ?`,
    [timestamp, body.release_reason || 'Released', body.notes || null, timestamp, id]
  );
  audit(userId, 'release_assignment', 'asset_assignment', id, before, { released_at: timestamp, ...body });
  return assignmentRows({ asset_id: before.asset_id }, companyId).find((assignment) => assignment.id === Number(id));
}

function assignmentSummary(companyId) {
  const stats = one(`
    SELECT
      COUNT(*) AS active_assignments,
      COUNT(DISTINCT aa.employee_id) AS assigned_employees,
      COUNT(DISTINCT aa.location) AS assigned_locations
    FROM asset_assignments aa
    JOIN assets a ON a.id = aa.asset_id
    WHERE aa.released_at IS NULL AND (? IS NULL OR a.entity_id = ?)
  `, [companyId || null, companyId || null]);
  const unassigned = one(`
    SELECT COUNT(*) AS count
    FROM assets a
    WHERE (? IS NULL OR a.entity_id = ?)
      AND NOT EXISTS (
        SELECT 1 FROM asset_assignments aa
        WHERE aa.asset_id = a.id AND aa.released_at IS NULL
      )
  `, [companyId || null, companyId || null]);
  return { ...stats, unassigned_assets: unassigned.count };
}

module.exports = {
  assignAsset,
  assignmentRows,
  assignmentSummary,
  employeeFromRow,
  listEmployees,
  releaseAssignment,
  upsertEmployee
};
