import { Router } from "express";

import { asyncHandler } from "../../lib/async-handler.js";
import { query, queryOne } from "../../lib/db.js";
import { HttpError } from "../../lib/http-error.js";
import { requireAuth, requireRole } from "../../middleware/auth.js";
import { validate } from "../../middleware/validate.js";
import { createDeviceSchema, deleteDeviceSchema, updateDeviceSchema } from "./devices.schemas.js";

export const devicesRouter = Router();

devicesRouter.use(requireAuth);

devicesRouter.get(
  "/",
  asyncHandler(async (_request, response) => {
    const items = await query<{
      id: string;
      imei: string;
      protocol_name: string;
      phone_number: string | null;
      status: string;
      last_seen: string | null;
      vehicle_id: string | null;
    }>(
      `
        select id, imei, protocol_name, phone_number, status, last_seen, vehicle_id
        from devices
        order by created_at desc
      `,
    );
    response.json({ items });
  }),
);

devicesRouter.get(
  "/unassigned",
  requireRole("admin"),
  asyncHandler(async (_request, response) => {
    const items = await query<{
      imei: string;
      protocol_name: string;
      first_seen: string;
      last_seen: string;
      raw_payload: string;
    }>(
      `
        select imei, protocol_name, first_seen, last_seen, raw_payload
        from unassigned_devices
        order by last_seen desc
      `,
    );
    response.json({ items });
  }),
);

devicesRouter.post(
  "/",
  requireRole("admin"),
  validate(createDeviceSchema),
  asyncHandler(async (request, response) => {
    const item = await queryOne<{
      id: string;
      imei: string;
      phone_number: string | null;
      vehicle_id: string | null;
    }>(
      `
        insert into devices (imei, protocol_name, phone_number, vehicle_id)
        values ($1, $2, $3, $4)
        on conflict (imei)
        do update set
          protocol_name = excluded.protocol_name,
          phone_number = coalesce(excluded.phone_number, devices.phone_number),
          vehicle_id = excluded.vehicle_id,
          updated_at = now()
        returning id, imei, phone_number, vehicle_id
      `,
      [request.body.imei, request.body.protocolName, request.body.phoneNumber ?? null, request.body.vehicleId ?? null],
    );

    if (!item) {
      throw new HttpError(500, "Failed to create device");
    }

    await query("delete from unassigned_devices where imei = $1", [request.body.imei]);

    response.status(201).json({ item });
  }),
);

devicesRouter.patch(
  "/:id",
  requireRole("admin"),
  validate(updateDeviceSchema),
  asyncHandler(async (request, response) => {
    const item = await queryOne<{
      id: string;
      imei: string;
      phone_number: string | null;
      vehicle_id: string | null;
    }>(
      `
        update devices
        set
          imei = $2,
          protocol_name = $3,
          phone_number = $4,
          vehicle_id = $5,
          updated_at = now()
        where id = $1
        returning id, imei, phone_number, vehicle_id
      `,
      [
        request.params.id,
        request.body.imei,
        request.body.protocolName,
        request.body.phoneNumber ?? null,
        request.body.vehicleId ?? null,
      ],
    );

    if (!item) {
      throw new HttpError(404, "Device not found");
    }

    response.json({ item });
  }),
);

devicesRouter.delete(
  "/:id",
  requireRole("admin"),
  validate(deleteDeviceSchema),
  asyncHandler(async (request, response) => {
    const removed = await queryOne<{ id: string }>(
      `
        delete from devices
        where id = $1
        returning id
      `,
      [request.params.id],
    );

    if (!removed) {
      throw new HttpError(404, "Device not found");
    }

    response.status(204).send();
  }),
);
