import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  OrganizerPreviewSchema,
  OrganizerFillSchema,
  OrganizerFillQuerySchema,
  OrganizerPreviewResponseSchema,
  OrganizerFillResponseSchema,
  OrganizerFillListResponseSchema,
} from '@meshitrack/shared';
import { OrganizerRepository } from './organizer.repository.js';
import { OrganizerService } from './organizer.service.js';

type AnyFillDeduction = {
  cabinetItemId: string;
  quantityTaken: number;
};

type AnyFillItem = {
  medicineId: string;
  medicineName: string;
  quantityNeeded: number;
  quantityTaken: number;
  wasShort: boolean;
  shortage: number;
  deductions: AnyFillDeduction[];
};

type AnyFillDoc = {
  _id: string | { toString: () => string };
  householdId: string;
  userId: string;
  regimenId: string;
  regimenName: string;
  numberOfDays: number;
  fillDate: string | Date | { toISOString: () => string };
  items: AnyFillItem[];
  status: string;
  notes?: string | null;
  createdAt: string | { toISOString: () => string };
  updatedAt: string | { toISOString: () => string };
};

function toStr(v: string | { toString: () => string }): string {
  return typeof v === 'string' ? v : v.toString();
}

function toIso(v: string | Date | { toISOString: () => string }): string {
  if (typeof v === 'string') return v;
  if (v instanceof Date) return v.toISOString();
  return v.toISOString();
}

function toFillResponse(doc: AnyFillDoc) {
  return {
    _id: toStr(doc._id),
    householdId: doc.householdId,
    userId: doc.userId,
    regimenId: doc.regimenId,
    regimenName: doc.regimenName,
    numberOfDays: doc.numberOfDays,
    fillDate: toIso(doc.fillDate),
    items: doc.items.map((item) => ({
      medicineId: item.medicineId,
      medicineName: item.medicineName,
      quantityNeeded: item.quantityNeeded,
      quantityTaken: item.quantityTaken,
      wasShort: item.wasShort,
      shortage: item.shortage,
      deductions: item.deductions.map((d) => ({
        cabinetItemId: d.cabinetItemId,
        quantityTaken: d.quantityTaken,
      })),
    })),
    status: doc.status,
    ...(doc.notes ? { notes: doc.notes } : {}),
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    organizerRepository: OrganizerRepository;
    organizerService: OrganizerService;
  }
}

export default fp(
  async (fastify) => {
    fastify.diContainer.register({
      organizerRepository: asClass(OrganizerRepository, { lifetime: Lifetime.SINGLETON }),
      organizerService: asClass(OrganizerService, { lifetime: Lifetime.SINGLETON }),
    });

    const app = fastify.withTypeProvider<ZodTypeProvider>();
    const householdParams = z.object({ householdId: z.string() });

    // GET /api/v1/households/:householdId/organizer/fills — list fill history
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/organizer/fills',
      schema: {
        params: householdParams,
        querystring: OrganizerFillQuerySchema,
        response: { 200: OrganizerFillListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('organizerService');
        const result = await service.listFills(
          request.params.householdId,
          request.user.keycloakId,
          request.query,
        );
        return reply.send({
          data: result.data.map(toFillResponse),
          pagination: result.pagination,
        });
      },
    });

    // GET /api/v1/households/:householdId/organizer/fills/:id — get fill details
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/organizer/fills/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 200: OrganizerFillResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('organizerService');
        const fill = await service.getFillById(request.params.id, request.params.householdId);
        return reply.send(toFillResponse(fill));
      },
    });

    // POST /api/v1/households/:householdId/organizer/preview — preview fill
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/organizer/preview',
      schema: {
        params: householdParams,
        body: OrganizerPreviewSchema,
        response: { 200: OrganizerPreviewResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('organizerService');
        const preview = await service.preview(
          request.params.householdId,
          request.user.keycloakId,
          request.body.regimenId,
          request.body.numberOfDays,
        );
        return reply.send(preview);
      },
    });

    // POST /api/v1/households/:householdId/organizer/fill — execute fill
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/organizer/fill',
      schema: {
        params: householdParams,
        body: OrganizerFillSchema,
        response: { 201: OrganizerFillResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('organizerService');
        const fill = await service.fill(
          request.params.householdId,
          request.user.keycloakId,
          request.body,
        );
        return reply.status(201).send(toFillResponse(fill));
      },
    });

    // POST /api/v1/households/:householdId/organizer/fills/:id/undo — reverse fill
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/organizer/fills/:id/undo',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 200: OrganizerFillResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('organizerService');
        const fill = await service.undoFill(
          request.params.householdId,
          request.params.id,
          request.user.keycloakId,
        );
        return reply.send(toFillResponse(fill));
      },
    });
  },
  {
    name: 'organizer-routes',
    dependencies: ['auth-plugin'],
  },
);
