import type { FastifyInstance, FastifyReply } from "fastify";
import type { MultipartFile } from "@fastify/multipart";
import { z } from "zod";
import {
  PetitionServiceError,
  createPetitionType,
  generatePetition,
  listPetitionTypes,
  rectifyPetition,
  softDeletePetitionExample,
  softDeletePetitionType,
  suggestPetitionTypeFieldValue,
  updatePetitionExample,
  updatePetitionType,
  uploadPetitionExample,
} from "../../lib/petitions.js";
import { requireSession } from "../../lib/auth.js";
import { getSessionProfile } from "../../lib/session.js";

const petitionTypeSchema = z.object({
  name: z.string().min(2).max(191),
  description: z.string().max(4_000).optional().or(z.literal("")),
  generationInstructions: z.string().max(12_000).optional().or(z.literal("")),
});

const petitionTypeAiSuggestionSchema = z.object({
  target: z.enum(["description", "generation_instructions"]),
  name: z.string().min(2).max(191),
  description: z.string().max(4_000).optional().or(z.literal("")),
  generationInstructions: z.string().max(12_000).optional().or(z.literal("")),
});

const petitionExampleSchema = z.object({
  petitionTypeId: z.string().uuid(),
  title: z.string().max(255).optional().or(z.literal("")),
  notes: z.string().max(4_000).optional().or(z.literal("")),
});

const generatePetitionSchema = z.object({
  petitionTypeId: z.string().uuid(),
  clientId: z.string().uuid(),
  instructions: z.string().max(16_000).optional().or(z.literal("")),
});

const petitionTypeParamSchema = z.object({
  petitionTypeId: z.string().uuid(),
});

const petitionExampleParamSchema = z.object({
  exampleId: z.string().uuid(),
});

function getMultipartFieldValue(
  fields: Record<string, MultipartFile["fields"][string]>,
  key: string,
) {
  const field = fields[key];

  if (!field || Array.isArray(field) || !("value" in field)) {
    return "";
  }

  return String(field.value ?? "").trim();
}

function handlePetitionServiceError(reply: FastifyReply, error: unknown): never {
  if (error instanceof PetitionServiceError) {
    if (error.statusCode === 404) {
      throw reply.notFound(error.message);
    }

    if (error.statusCode === 409) {
      throw reply.conflict(error.message);
    }

    if (error.statusCode === 400) {
      throw reply.badRequest(error.message);
    }

    throw reply.internalServerError(error.message);
  }

  throw error;
}

export async function registerPetitionRoutes(app: FastifyInstance) {
  app.get("/types", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    return listPetitionTypes({
      lawFirmId: profile.lawFirm.id,
    });
  });

  app.post("/types", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const payload = petitionTypeSchema.parse(request.body);

    try {
      const created = await createPetitionType({
        lawFirmId: profile.lawFirm.id,
        actorUserId: profile.user.id,
        name: payload.name,
        description: payload.description,
        generationInstructions: payload.generationInstructions,
      });

      return reply.code(201).send(created);
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.patch("/types/:petitionTypeId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const { petitionTypeId } = petitionTypeParamSchema.parse(request.params);
    const payload = petitionTypeSchema.parse(request.body);

    try {
      return await updatePetitionType({
        lawFirmId: profile.lawFirm.id,
        petitionTypeId,
        name: payload.name,
        description: payload.description,
        generationInstructions: payload.generationInstructions,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.post("/types/ai-suggestion", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const payload = petitionTypeAiSuggestionSchema.parse(request.body);

    try {
      return await suggestPetitionTypeFieldValue({
        lawFirmId: profile.lawFirm.id,
        target: payload.target,
        name: payload.name,
        description: payload.description,
        generationInstructions: payload.generationInstructions,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.delete("/types/:petitionTypeId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const { petitionTypeId } = petitionTypeParamSchema.parse(request.params);

    try {
      return await softDeletePetitionType({
        lawFirmId: profile.lawFirm.id,
        petitionTypeId,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.post("/types/:petitionTypeId/examples", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const { petitionTypeId } = petitionTypeParamSchema.parse(request.params);
    const file = await request.file();

    if (!file) {
      throw reply.badRequest("A file upload is required.");
    }

    const bytes = await file.toBuffer();

    try {
      const created = await uploadPetitionExample({
        lawFirmId: profile.lawFirm.id,
        actorUserId: profile.user.id,
        petitionTypeId,
        title: getMultipartFieldValue(file.fields, "title"),
        notes: getMultipartFieldValue(file.fields, "notes"),
        fileName: file.filename,
        mimeType: file.mimetype,
        bytes,
      });

      return reply.code(201).send(created);
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.patch("/examples/:exampleId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const { exampleId } = petitionExampleParamSchema.parse(request.params);
    const payload = petitionExampleSchema.parse(request.body);

    try {
      return await updatePetitionExample({
        lawFirmId: profile.lawFirm.id,
        exampleId,
        petitionTypeId: payload.petitionTypeId,
        title: payload.title,
        notes: payload.notes,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.delete("/examples/:exampleId", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const { exampleId } = petitionExampleParamSchema.parse(request.params);

    try {
      return await softDeletePetitionExample({
        lawFirmId: profile.lawFirm.id,
        exampleId,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.post("/generate", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const payload = generatePetitionSchema.parse(request.body);

    try {
      return await generatePetition({
        lawFirmId: profile.lawFirm.id,
        actorUserId: profile.user.id,
        petitionTypeId: payload.petitionTypeId,
        clientId: payload.clientId,
        instructions: payload.instructions,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });

  app.post("/rectify", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const file = await request.file();

    if (!file) {
      throw reply.badRequest("A file upload is required.");
    }

    const instructions = getMultipartFieldValue(file.fields, "instructions");

    if (!instructions) {
      throw reply.badRequest("instructions is required.");
    }

    const petitionTypeId = getMultipartFieldValue(file.fields, "petitionTypeId");
    const clientId = getMultipartFieldValue(file.fields, "clientId");
    const bytes = await file.toBuffer();

    try {
      return await rectifyPetition({
        lawFirmId: profile.lawFirm.id,
        actorUserId: profile.user.id,
        fileName: file.filename,
        mimeType: file.mimetype,
        bytes,
        instructions,
        petitionTypeId: petitionTypeId || null,
        clientId: clientId || null,
      });
    } catch (error) {
      return handlePetitionServiceError(reply, error);
    }
  });
}
