import { usePrisma } from "../utils/prisma";
import pg from "pg";

const { Pool } = pg;

let _mdmPool: pg.Pool | null = null;

/**
 * Lazily initialised pg.Pool against the NanoMDM Postgres.
 * Connection string comes from runtimeConfig.mdmDatabaseUrl.
 */
function useMdmPool(): pg.Pool {
  if (_mdmPool) return _mdmPool;

  const config = useRuntimeConfig();
  const connectionString = (config as any).mdmDatabaseUrl;

  if (!connectionString) {
    throw new Error("MDM_DATABASE_URL not configured");
  }

  _mdmPool = new Pool({
    connectionString,
    // NanoMDM queries are point lookups — keep pool small.
    max: 5,
    connectionTimeoutMillis: 5000,
    query_timeout: 5000,
  });

  return _mdmPool;
}

export interface UserDeviceMdmRecord {
  id: string;
  userId: string;
  deviceId: string;
  platform: string;
  mdmId: string | null;
}

const USER_DEVICE_MDM_SELECT = {
  id: true,
  userId: true,
  deviceId: true,
  platform: true,
  mdmId: true,
} as const;

const USER_DEVICE_MDM_HEALTH_SELECT = {
  id: true,
  userId: true,
  deviceId: true,
  platform: true,
  mdmId: true,
  mdmEnrolled: true,
  mdmSupervised: true,
  mdmLastSeenAt: true,
} as const;

/**
 * Find a user's iOS device by Capacitor deviceId.
 */
export async function getUserDeviceByDeviceId(
  userId: string,
  deviceId: string,
  platform: string = "ios",
): Promise<UserDeviceMdmRecord | null> {
  const db = usePrisma();
  return db.userDevice.findFirst({
    where: { userId, deviceId, platform },
    select: USER_DEVICE_MDM_SELECT,
  });
}

/**
 * Persist the NanoMDM UDID for a user's device.
 */
export async function setUserDeviceMdmId(
  userId: string,
  deviceId: string,
  mdmId: string,
): Promise<void> {
  const db = usePrisma();
  await db.userDevice.updateMany({
    where: { userId, deviceId, platform: "ios" },
    data: { mdmId },
  });
}

/**
 * Clear the stored NanoMDM UDID (e.g. device no longer enrolled).
 */
export async function clearUserDeviceMdmId(
  userId: string,
  deviceId: string,
): Promise<void> {
  const db = usePrisma();
  await db.userDevice.updateMany({
    where: { userId, deviceId, platform: "ios" },
    data: { mdmId: null },
  });
}

/**
 * Load all iOS devices that have a NanoMDM UDID link.
 */
export async function getLinkedUserDevices(): Promise<
  UserDeviceMdmHealthRecord[]
> {
  const db = usePrisma();
  return db.userDevice.findMany({
    where: { platform: "ios", mdmId: { not: null } },
    select: USER_DEVICE_MDM_HEALTH_SELECT,
  });
}

export interface MdmDeviceStatus {
  enrolled: boolean;
  exists: boolean;
  company: string | null;
  supervised: boolean;
  tokenUpdateAt: Date | null;
  lastAckAt: Date | null;
  lastAppPushAt: Date | null;
}

export interface MdmEnrollmentStatus {
  enrolled: boolean;
  supervised: boolean;
  lastSeenAt: Date | null;
}

export interface UserDeviceMdmHealthRecord {
  id: string;
  userId: string;
  deviceId: string;
  platform: string;
  mdmId: string | null;
  mdmEnrolled: boolean | null;
  mdmSupervised: boolean | null;
  mdmLastSeenAt: Date | null;
}

/**
 * Query NanoMDM Postgres for a device by UDID.
 *
 * Throws if the MDM DB is unreachable — callers should treat this as an
 * infra/runtime error and not cache a negative result.
 */
export async function getMdmStatusByUdid(
  udid: string,
): Promise<MdmDeviceStatus> {
  const pool = useMdmPool();

  // Defensive: only raw parameters reach the query layer below.
  const result = await pool.query<{
    unlock_token: Buffer | null;
    token_update_at: Date | null;
    last_ack: Date | null;
    last_app_push_at: Date | null;
    enrolled: boolean;
  }>(
    `SELECT
      d.unlock_token,
      d.token_update_at,
      COALESCE(e.enabled = TRUE, FALSE) AS enrolled,
      (SELECT max(updated_at) FROM command_results WHERE id = d.id) AS last_ack,
      (SELECT max(r.updated_at)
       FROM command_results r
       JOIN commands c ON c.command_uuid = r.command_uuid
       WHERE r.id = d.id
         AND c.request_type = 'InstallApplication'
         AND r.status = 'Acknowledged') AS last_app_push_at
     FROM devices d
     LEFT JOIN enrollments e ON e.device_id = d.id
     WHERE d.id = $1`,
    [udid],
  );

  const row = result.rows[0];
  const exists = row !== undefined;
  const enrolled = row?.enrolled ?? false;

  return {
    enrolled,
    exists,
    company: enrolled ? "ReBreak" : null,
    supervised: exists && row?.unlock_token != null,
    tokenUpdateAt: row?.token_update_at ?? null,
    lastAckAt: row?.last_ack ?? null,
    lastAppPushAt: row?.last_app_push_at ?? null,
  };
}

/**
 * Bulk-query NanoMDM for enrollment/supervision/last-seen status.
 * Returns a map keyed by UDID. Missing devices are omitted.
 *
 * Throws if the MDM DB is unreachable — callers should treat this as an
 * infra/runtime error and not cache a negative result.
 */
export async function getMdmEnrollmentStatusesByUdids(
  udids: string[],
): Promise<Map<string, MdmEnrollmentStatus>> {
  if (udids.length === 0) {
    return new Map();
  }

  const pool = useMdmPool();
  const result = await pool.query<{
    udid: string;
    enrolled: boolean;
    supervised: boolean;
    last_seen_at: Date | null;
  }>(
    `SELECT
       d.id AS udid,
       COALESCE(e.enabled = TRUE, FALSE) AS enrolled,
       (d.unlock_token IS NOT NULL) AS supervised,
       e.last_seen_at
     FROM devices d
     LEFT JOIN enrollments e ON e.device_id = d.id
     WHERE d.id = ANY($1::text[])`,
    [udids],
  );

  const map = new Map<string, MdmEnrollmentStatus>();
  for (const row of result.rows) {
    map.set(row.udid, {
      enrolled: row.enrolled,
      supervised: row.supervised,
      lastSeenAt: row.last_seen_at,
    });
  }
  return map;
}

/**
 * Persist mirrored MDM health status on a UserDevice row.
 */
export async function updateUserDeviceMdmHealth(
  id: string,
  status: MdmEnrollmentStatus,
): Promise<void> {
  const db = usePrisma();
  await db.userDevice.update({
    where: { id },
    data: {
      mdmEnrolled: status.enrolled,
      mdmSupervised: status.supervised,
      mdmLastSeenAt: status.lastSeenAt,
    },
  });
}
