import type { Pool } from 'pg';
import { pgGet, pgAll, pgRun } from '@/shared/db';
import { conflict, notFound } from '@/shared/http/errors';

import type { Device, DeviceDraft } from './types';

interface Row {
  id: string;
  name: string;
  hardware_id: string;
  token: string;
  platform: string;
  registered_at: string;
  last_seen_at: string | null;
  revoked_at: string | null;
}

const hydrate = (r: Row): Device => ({
  id: r.id,
  name: r.name,
  hardwareId: r.hardware_id,
  token: r.token,
  platform: r.platform,
  registeredAt: r.registered_at,
  lastSeenAt: r.last_seen_at,
  revokedAt: r.revoked_at,
});

export async function listDevices(pool: Pool): Promise<readonly Device[]> {
  const rows = await pgAll<Row>(pool, 'SELECT * FROM macsync.devices ORDER BY registered_at DESC');
  return rows.map(hydrate);
}

export async function getDevice(pool: Pool, id: string): Promise<Device> {
  const row = await pgGet<Row>(pool, 'SELECT * FROM macsync.devices WHERE id = $1', [id]);
  if (!row) throw notFound('device_not_found', `device ${id} not found`);
  return hydrate(row);
}

export async function createDevice(pool: Pool, draft: DeviceDraft): Promise<Device> {
  const existing = await pgGet<{ id: string }>(pool, 'SELECT id FROM macsync.devices WHERE id = $1', [draft.id]);
  if (existing) throw conflict('device_exists', `device ${draft.id} already registered`);
  await pgRun(
    pool,
    'INSERT INTO macsync.devices (id, name, hardware_id, token, platform) VALUES ($1, $2, $3, $4, $5)',
    [draft.id, draft.name, draft.hardwareId, draft.token, draft.platform],
  );
  return getDevice(pool, draft.id);
}

/**
 * Idempotent registration by hardware_id.
 * - If no existing (non-revoked) row exists, creates one with the supplied token.
 * - If an existing row is found, returns it unchanged (does NOT mint a new token).
 * Returns the device row and a boolean indicating whether the row was newly created.
 */
export async function upsertDeviceByHardwareId(
  pool: Pool,
  draft: DeviceDraft,
): Promise<{ device: Device; created: boolean }> {
  const existing = await pgGet<Row>(
    pool,
    'SELECT * FROM macsync.devices WHERE hardware_id = $1 AND revoked_at IS NULL',
    [draft.hardwareId],
  );
  if (existing) {
    await touchDevice(pool, existing.id);
    return { device: hydrate(existing), created: false };
  }
  const device = await createDevice(pool, draft);
  return { device, created: true };
}

export async function touchDevice(pool: Pool, id: string): Promise<void> {
  await pgRun(pool, 'UPDATE macsync.devices SET last_seen_at = now() WHERE id = $1', [id]);
}

export async function revokeDevice(pool: Pool, id: string): Promise<void> {
  const result = await pgAll<{ changes: number }>(
    pool,
    'UPDATE macsync.devices SET revoked_at = now() WHERE id = $1 AND revoked_at IS NULL RETURNING 1',
    [id],
  );
  if (result.length === 0) throw notFound('device_not_found', `device ${id} not found or already revoked`);
}
