import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreateCabinetItemSchema,
  UpdateCabinetItemSchema,
  AdjustQuantitySchema,
  CabinetQuerySchema,
  CabinetItemResponseSchema,
  CabinetItemListResponseSchema,
  CabinetSummaryResponseSchema,
  DiscardCabinetItemSchema,
} from '@meshitrack/shared';
import { CabinetRepository } from './cabinet.repository.js';
import { CabinetService } from './cabinet.service.js';

type AnyCabinetDoc = {
  _id: string | { toString: () => string };
  householdId: string;
  medicineId: string;
  medicineName: string;
  medicineStrength: number;
  medicineStrengthUnit: string;
  medicineForm: string;
  medicineProductId?: string | null;
  medicineProductBrand?: string | null;
  concentration?: number | null;
  concentrationUnit?: string | null;
  quantity: number;
  unit: string;
  expirationDate?: Date | string | null;
  status: string;
  purchaseDate?: Date | string | null;
  unitPrice?: number | null;
  totalPrice?: number | null;
  currency?: string | null;
  storeId?: string | null;
  storeName?: string | null;
  notes?: string | null;
  createdBy: string;
  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 toOptIso(v: Date | string | null | undefined): string | undefined {
  /* v8 ignore next */
  if (!v) return undefined;
  if (typeof v === 'string') return v;
  return v.toISOString();
}

function toCabinetItemResponse(doc: AnyCabinetDoc): z.infer<typeof CabinetItemResponseSchema> {
  return {
    _id: toStr(doc._id),
    householdId: doc.householdId,
    medicineId: doc.medicineId,
    medicineName: doc.medicineName,
    medicineStrength: doc.medicineStrength,
    medicineStrengthUnit: doc.medicineStrengthUnit,
    medicineForm: doc.medicineForm,
    ...(doc.medicineProductId ? { medicineProductId: doc.medicineProductId } : {}),
    ...(doc.medicineProductBrand ? { medicineProductBrand: doc.medicineProductBrand } : {}),
    ...(doc.concentration != null ? { concentration: doc.concentration } : {}),
    ...(doc.concentrationUnit ? { concentrationUnit: doc.concentrationUnit } : {}),
    quantity: doc.quantity,
    unit: doc.unit,
    ...(doc.expirationDate ? { expirationDate: toOptIso(doc.expirationDate) } : {}),
    status: doc.status,
    ...(doc.purchaseDate ? { purchaseDate: toOptIso(doc.purchaseDate) } : {}),
    ...(doc.unitPrice != null ? { unitPrice: doc.unitPrice } : {}),
    ...(doc.totalPrice != null ? { totalPrice: doc.totalPrice } : {}),
    ...(doc.currency ? { currency: doc.currency } : {}),
    ...(doc.storeId ? { storeId: doc.storeId } : {}),
    ...(doc.storeName ? { storeName: doc.storeName } : {}),
    ...(doc.notes ? { notes: doc.notes } : {}),
    createdBy: doc.createdBy,
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    cabinetRepository: CabinetRepository;
    cabinetService: CabinetService;
  }
}

export default fp(
  async (fastify) => {
    fastify.diContainer.register({
      cabinetRepository: asClass(CabinetRepository, { lifetime: Lifetime.SINGLETON }),
      cabinetService: asClass(CabinetService, { lifetime: Lifetime.SINGLETON }),
    });

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

    // GET /api/v1/households/:householdId/cabinet — list items
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/cabinet',
      schema: {
        params: householdParams,
        querystring: CabinetQuerySchema,
        response: { 200: CabinetItemListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const result = await service.list(request.params.householdId, request.query);
        return reply.send({
          data: result.data.map(toCabinetItemResponse),
          pagination: result.pagination,
        });
      },
    });

    // GET /api/v1/households/:householdId/cabinet/summary — aggregate per medicine
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/cabinet/summary',
      schema: {
        params: householdParams,
        response: { 200: CabinetSummaryResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const data = await service.getSummary(request.params.householdId);
        return reply.send({ data });
      },
    });

    // GET /api/v1/households/:householdId/cabinet/expiring-soon — items expiring within N days
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/cabinet/expiring-soon',
      schema: {
        params: householdParams,
        querystring: z.object({
          days: z.coerce.number().int().min(1).max(365).default(30),
        }),
        response: {
          200: z.object({ data: z.array(CabinetItemResponseSchema) }),
        },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const items = await service.getExpiringSoon(request.params.householdId, request.query.days);
        return reply.send({ data: items.map(toCabinetItemResponse) });
      },
    });

    // GET /api/v1/households/:householdId/cabinet/:id — get single item
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/cabinet/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 200: CabinetItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const item = await service.getById(request.params.id, request.params.householdId);
        return reply.send(toCabinetItemResponse(item));
      },
    });

    // POST /api/v1/households/:householdId/cabinet — add item
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/cabinet',
      schema: {
        params: householdParams,
        body: CreateCabinetItemSchema,
        response: { 201: CabinetItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const item = await service.addItem(
          request.body,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.status(201).send(toCabinetItemResponse(item));
      },
    });

    // PATCH /api/v1/households/:householdId/cabinet/:id — update item
    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId/cabinet/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        body: UpdateCabinetItemSchema,
        response: { 200: CabinetItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const item = await service.update(
          request.params.id,
          request.params.householdId,
          request.body,
          request.user.keycloakId,
        );
        return reply.send(toCabinetItemResponse(item));
      },
    });

    // POST /api/v1/households/:householdId/cabinet/:id/adjust — adjust quantity
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/cabinet/:id/adjust',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        body: AdjustQuantitySchema,
        response: { 200: CabinetItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const item = await service.adjustQuantity(
          request.params.id,
          request.params.householdId,
          request.body.delta,
          request.user.keycloakId,
          request.body.reason,
        );
        return reply.send(toCabinetItemResponse(item));
      },
    });

    // DELETE /api/v1/households/:householdId/cabinet/:id — soft delete
    app.route({
      method: 'DELETE',
      url: '/api/v1/households/:householdId/cabinet/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 204: z.undefined() },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        await service.delete(
          request.params.id,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.status(204).send();
      },
    });

    // POST /api/v1/households/:householdId/cabinet/:id/discard — discard item
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/cabinet/:id/discard',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        body: DiscardCabinetItemSchema,
        response: { 200: CabinetItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('cabinetService');
        const item = await service.discard(
          request.params.id,
          request.params.householdId,
          request.user.keycloakId,
          request.body.reason,
          request.body.notes,
        );
        return reply.send(toCabinetItemResponse(item));
      },
    });
  },
  {
    name: 'cabinet-routes',
    dependencies: ['auth-plugin'],
  },
);
