import { db } from "@minmon/db";
import { domain, service } from "@minmon/db";
import { desc, eq } from "drizzle-orm";

import { runAndPersistDomainCheck } from "../lib/domain-check";
import { permissionedProcedure, router } from "../index";
import {
  byIdSchema,
  domainCreateSchema,
  domainListSchema,
  domainUpdateSchema,
} from "./schemas";
import {
  createEntityId,
  requireDomain,
  requireService,
  requireServiceWithServer,
  stripUndefined,
} from "./utils";

export const domainsRouter = router({
  list: permissionedProcedure("domains.read").input(domainListSchema).query(async ({ input }) => {
    if (!input?.serviceId) {
      return db.select().from(domain).orderBy(desc(domain.createdAt), desc(domain.id));
    }

    await requireService(input.serviceId);

    return db
      .select()
      .from(domain)
      .where(eq(domain.serviceId, input.serviceId))
      .orderBy(desc(domain.createdAt), desc(domain.id));
  }),

  byId: permissionedProcedure("domains.read").input(byIdSchema).query(async ({ input }) => {
    return requireDomain(input.id);
  }),

  create: permissionedProcedure("domains.create").input(domainCreateSchema).mutation(async ({ input }) => {
    const { service: parentService, server: parentServer } = await requireServiceWithServer(input.serviceId);

    const expectedServerIp = input.expectedServerIp ?? parentServer.primaryIpAddress;

    const [created] = await db
      .insert(domain)
      .values({
        id: createEntityId(),
        serviceId: parentService.id,
        name: input.name,
        expectedServerIp,
        remarks: input.remarks,
      })
      .returning();

    return created;
  }),

  update: permissionedProcedure("domains.update").input(domainUpdateSchema).mutation(async ({ input }) => {
    const { id, serviceId, ...changes } = input;
    const existing = await requireDomain(id);

    let nextExpectedServerIp = changes.expectedServerIp;

    if (serviceId && serviceId !== existing.serviceId) {
      const { server: parentServer } = await requireServiceWithServer(serviceId);
      if (!nextExpectedServerIp) {
        nextExpectedServerIp = parentServer.primaryIpAddress;
      }
    }

    const [updated] = await db
      .update(domain)
      .set(stripUndefined({ serviceId, expectedServerIp: nextExpectedServerIp, ...changes }))
      .where(eq(domain.id, id))
      .returning();

    return updated;
  }),

  delete: permissionedProcedure("domains.delete").input(byIdSchema).mutation(async ({ input }) => {
    await requireDomain(input.id);
    await db.delete(domain).where(eq(domain.id, input.id));
    return { id: input.id };
  }),

  check: permissionedProcedure("domains.check").input(byIdSchema).mutation(async ({ input }) => {
    const existing = await requireDomain(input.id);

    const result = await runAndPersistDomainCheck(db, {
      domainId: existing.id,
      name: existing.name,
      expectedServerIp: existing.expectedServerIp,
    });

    return {
      domain: result.domain ?? existing,
      check: result.result,
    };
  }),

  serviceOptions: permissionedProcedure("services.read").query(async () => {
    return db
      .select({
        id: service.id,
        name: service.name,
        serverId: service.serverId,
      })
      .from(service)
      .orderBy(desc(service.updatedAt));
  }),
});
