import { NOTIFICATION_KINDS } from '@pig/core';
import type { Database } from '@pig/db';
import { accounts, channelLinks, notificationOutbox } from '@pig/db';
import { and, desc, eq, inArray } from 'drizzle-orm';
import { Hono } from 'hono';
import { z } from 'zod';
import { requireCapability } from '../lib/auth';
import {
  bodylessMutation,
  MutationError,
  mutation,
  type ApiEnv,
  type MutationDefinition,
} from '../lib/mutation';
import { normaliseBuzzRelayUrl } from '../services/buzz';

const buzzLinkSchema = z
  .object({
    channelId: z.string().uuid(),
    channelName: z.string().trim().min(1).max(120).nullable().optional(),
    accountId: z.string().uuid(),
    notifyOn: z.array(z.enum(NOTIFICATION_KINDS)).min(1).default([...NOTIFICATION_KINDS]),
  })
  .strict();
const emptyMutationSchema = z.object({}).strict();

export function buzzWorkspaceId(relayUrl: string): string {
  return new URL(normaliseBuzzRelayUrl(relayUrl)).host;
}

export function createBuzzLinkMutationDefinition(
  workspaceId: string,
): MutationDefinition<typeof buzzLinkSchema, typeof channelLinks.$inferSelect> {
  return {
    schema: buzzLinkSchema,
    permission: { capability: 'settings:admin' },
    invalidMessage: 'Invalid Buzz channel link.',
    async mutate({ input, principal, tx, now }) {
      const [account] = await tx
        .select({ id: accounts.id, name: accounts.name })
        .from(accounts)
        .where(eq(accounts.id, input.accountId))
        .limit(1);
      if (!account) throw MutationError.notFound('Account');

      const [link] = await tx
        .insert(channelLinks)
        .values({
          ...input,
          platform: 'buzz',
          workspaceId,
          linkedByUserId: principal.userId,
          updatedAt: now,
        })
        .onConflictDoUpdate({
          target: [channelLinks.platform, channelLinks.workspaceId, channelLinks.channelId],
          set: {
            accountId: input.accountId,
            channelName: input.channelName,
            notifyOn: input.notifyOn,
            linkedByUserId: principal.userId,
            updatedAt: now,
          },
        })
        .returning();
      if (!link) throw new MutationError('write_failed', 'Buzz channel was not linked.', 409);
      return {
        data: link,
        activity: {
          type: 'buzz',
          subject: `Linked Buzz channel to ${account.name}`,
          accountId: account.id,
          meta: { action: 'linked', integration: 'buzz', channelId: link.channelId },
        },
      };
    },
  };
}

function createBuzzUnlinkMutationDefinition(): MutationDefinition<
  typeof emptyMutationSchema,
  { deleted: true }
> {
  return {
    schema: emptyMutationSchema,
    permission: { capability: 'settings:admin' },
    invalidMessage: 'Invalid Buzz unlink request.',
    async mutate({ params, tx }) {
      if (!params.id) throw MutationError.notFound('Buzz channel link');
      const [link] = await tx
        .select()
        .from(channelLinks)
        .where(and(eq(channelLinks.id, params.id), eq(channelLinks.platform, 'buzz')))
        .limit(1);
      if (!link) throw MutationError.notFound('Buzz channel link');

      await tx
        .update(notificationOutbox)
        .set({ status: 'cancelled', leasedBy: null, leasedUntil: null })
        .where(
          and(
            eq(notificationOutbox.linkId, link.id),
            inArray(notificationOutbox.status, ['pending', 'leased']),
          ),
        );
      await tx.delete(channelLinks).where(eq(channelLinks.id, link.id));
      return {
        data: { deleted: true as const },
        activity: {
          type: 'buzz',
          subject: 'Unlinked Buzz channel',
          accountId: link.accountId ?? undefined,
          meta: { action: 'unlinked', integration: 'buzz', channelId: link.channelId },
        },
      };
    },
  };
}

export function createBuzzRoutes(db: Database, relayUrl: string): Hono<ApiEnv> {
  const app = new Hono<ApiEnv>();
  const workspaceId = buzzWorkspaceId(relayUrl);

  app.get('/api/integrations/buzz/channel-links', async (c) => {
    requireCapability(c.get('principal'), 'settings:admin');
    const links = await db
      .select({ link: channelLinks, accountName: accounts.name })
      .from(channelLinks)
      .innerJoin(accounts, eq(accounts.id, channelLinks.accountId))
      .where(and(eq(channelLinks.platform, 'buzz'), eq(channelLinks.workspaceId, workspaceId)))
      .orderBy(desc(channelLinks.updatedAt));
    return c.json(links);
  });
  app.post(
    '/api/integrations/buzz/channel-links',
    mutation(db, createBuzzLinkMutationDefinition(workspaceId)),
  );
  app.delete(
    '/api/integrations/buzz/channel-links/:id',
    bodylessMutation(db, createBuzzUnlinkMutationDefinition()),
  );
  return app;
}
