import type { FastifyApp } from '@bytelyst/fastify-core';
import { BadRequestError, NotFoundError } from '../../lib/errors.js';
import { extractAuth } from '../../lib/auth.js';
import { PRODUCT_ID } from '../../lib/product-config.js';
import * as repo from './repository.js';
import { CreateNoteSchema, ListNotesQuerySchema, UpdateNoteSchema, type NoteDoc } from './types.js';

type RouteApp = Omit<FastifyApp, 'setReadyState' | 'isReadyState'>;

export async function noteRoutes(app: RouteApp) {
  app.get('/notes/search', async req => {
    const auth = await extractAuth(req);
    const parsed = ListNotesQuerySchema.safeParse(req.query);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; '));
    }

    const result = await repo.listNotes(auth.sub, PRODUCT_ID, parsed.data);
    return {
      query: parsed.data.search ?? null,
      ...result,
      limit: parsed.data.limit,
      offset: parsed.data.offset,
    };
  });

  app.get('/notes', async req => {
    const auth = await extractAuth(req);
    const parsed = ListNotesQuerySchema.safeParse(req.query);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; '));
    }

    const result = await repo.listNotes(auth.sub, PRODUCT_ID, parsed.data);
    return { ...result, limit: parsed.data.limit, offset: parsed.data.offset };
  });

  app.get('/notes/:id', async req => {
    const auth = await extractAuth(req);
    const { id } = req.params as { id: string };
    const workspaceId = (req.query as { workspaceId?: string }).workspaceId;

    if (!workspaceId) {
      throw new BadRequestError('workspaceId is required');
    }

    const note = await repo.getNote(id, workspaceId);
    if (!note || note.userId !== auth.sub || note.productId !== PRODUCT_ID) {
      throw new NotFoundError('Note not found');
    }

    return note;
  });

  app.post('/notes', async (req, reply) => {
    const auth = await extractAuth(req);
    const parsed = CreateNoteSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(
        parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')
      );
    }

    const now = new Date().toISOString();
    const doc: NoteDoc = {
      id: parsed.data.id,
      productId: PRODUCT_ID,
      workspaceId: parsed.data.workspaceId,
      userId: auth.sub,
      title: parsed.data.title,
      body: parsed.data.body,
      status: 'draft',
      tags: parsed.data.tags,
      links: parsed.data.links,
      sourceType: parsed.data.sourceType,
      sourceUri: parsed.data.sourceUri,
      createdAt: now,
      updatedAt: now,
      createdBy: auth.sub,
      updatedBy: auth.sub,
      agentId: parsed.data.agentId,
    };

    const created = await repo.createNote(doc);
    reply.code(201);
    return created;
  });

  app.patch('/notes/:id', async req => {
    const auth = await extractAuth(req);
    const { id } = req.params as { id: string };
    const workspaceId = (req.query as { workspaceId?: string }).workspaceId;

    if (!workspaceId) {
      throw new BadRequestError('workspaceId is required');
    }

    const parsed = UpdateNoteSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(
        parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')
      );
    }

    const existing = await repo.getNote(id, workspaceId);
    if (!existing || existing.userId !== auth.sub || existing.productId !== PRODUCT_ID) {
      throw new NotFoundError('Note not found');
    }

    const updated = await repo.updateNote(id, workspaceId, {
      ...parsed.data,
      updatedAt: new Date().toISOString(),
      updatedBy: auth.sub,
    });

    if (!updated) {
      throw new NotFoundError('Note not found');
    }

    return updated;
  });

  app.post('/notes/:id/archive', async req => {
    const auth = await extractAuth(req);
    const { id } = req.params as { id: string };
    const workspaceId = (req.body as { workspaceId?: string } | undefined)?.workspaceId;

    if (!workspaceId) {
      throw new BadRequestError('workspaceId is required');
    }

    const existing = await repo.getNote(id, workspaceId);
    if (!existing || existing.userId !== auth.sub || existing.productId !== PRODUCT_ID) {
      throw new NotFoundError('Note not found');
    }

    const updated = await repo.updateNote(id, workspaceId, {
      status: 'archived',
      updatedAt: new Date().toISOString(),
      updatedBy: auth.sub,
    });

    if (!updated) {
      throw new NotFoundError('Note not found');
    }

    return updated;
  });
}
