import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreateMedicineProductSchema,
  UpdateMedicineProductSchema,
  MedicineProductResponseSchema,
  MedicineProductListResponseSchema,
} from '@meshitrack/shared';
import { MedicineProductsRepository } from './medicine-products.repository.js';
import { MedicineProductsService } from './medicine-products.service.js';

type AnyProductDoc = {
  _id: string | { toString: () => string };
  householdId: string;
  medicineId: string;
  medicineName: string;
  brand: string;
  manufacturer?: string | null;
  packageSize: number;
  packageUnit: string;
  concentration?: number | null;
  concentrationUnit?: string | null;
  imageUrl?: string | null;
  notes?: string | null;
  source: string;
  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 | { toISOString: () => string }): string {
  return typeof v === 'string' ? v : v.toISOString();
}

function toProductResponse(doc: AnyProductDoc): z.infer<typeof MedicineProductResponseSchema> {
  return {
    _id: toStr(doc._id),
    householdId: doc.householdId,
    medicineId: doc.medicineId,
    medicineName: doc.medicineName,
    brand: doc.brand,
    ...(doc.manufacturer ? { manufacturer: doc.manufacturer } : {}),
    packageSize: doc.packageSize,
    packageUnit: doc.packageUnit,
    ...(doc.concentration ? { concentration: doc.concentration } : {}),
    ...(doc.concentrationUnit ? { concentrationUnit: doc.concentrationUnit } : {}),
    ...(doc.imageUrl ? { imageUrl: doc.imageUrl } : {}),
    ...(doc.notes ? { notes: doc.notes } : {}),
    source: doc.source,
    createdBy: doc.createdBy,
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    medicineProductsRepository: MedicineProductsRepository;
    medicineProductsService: MedicineProductsService;
  }
}

export default fp(
  async (fastify) => {
    fastify.diContainer.register({
      medicineProductsRepository: asClass(MedicineProductsRepository, {
        lifetime: Lifetime.SINGLETON,
      }),
      medicineProductsService: asClass(MedicineProductsService, { lifetime: Lifetime.SINGLETON }),
    });

    const app = fastify.withTypeProvider<ZodTypeProvider>();
    const householdParams = z.object({ householdId: z.string() });
    const paginationQuery = z.object({
      cursor: z.string().optional(),
      limit: z.coerce.number().int().min(1).max(100).default(20),
    });

    // GET /api/v1/households/:householdId/medicines/:medicineId/products — list by medicine
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/medicines/:medicineId/products',
      schema: {
        params: householdParams.extend({ medicineId: z.string() }),
        querystring: paginationQuery,
        response: { 200: MedicineProductListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('medicineProductsService');
        const result = await service.listByMedicine(
          request.params.householdId,
          request.params.medicineId,
          request.query,
        );
        return reply.send({
          data: result.data.map(toProductResponse),
          pagination: result.pagination,
        });
      },
    });

    // GET /api/v1/households/:householdId/medicine-products/:id — get by id
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/medicine-products/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 200: MedicineProductResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('medicineProductsService');
        const product = await service.getById(request.params.id, request.params.householdId);
        return reply.send(toProductResponse(product));
      },
    });

    // POST /api/v1/households/:householdId/medicines/:medicineId/products — create
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/medicines/:medicineId/products',
      schema: {
        params: householdParams.extend({ medicineId: z.string() }),
        body: CreateMedicineProductSchema,
        response: { 201: MedicineProductResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('medicineProductsService');
        const product = await service.create(
          request.body,
          request.params.householdId,
          request.params.medicineId,
          request.user.keycloakId,
        );
        return reply.status(201).send(toProductResponse(product));
      },
    });

    // PATCH /api/v1/households/:householdId/medicine-products/:id — update
    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId/medicine-products/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        body: UpdateMedicineProductSchema,
        response: { 200: MedicineProductResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('medicineProductsService');
        const product = await service.update(
          request.params.id,
          request.params.householdId,
          request.body,
        );
        return reply.send(toProductResponse(product));
      },
    });

    // DELETE /api/v1/households/:householdId/medicine-products/:id — soft delete
    app.route({
      method: 'DELETE',
      url: '/api/v1/households/:householdId/medicine-products/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 204: z.undefined() },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('medicineProductsService');
        await service.delete(request.params.id, request.params.householdId);
        return reply.status(204).send();
      },
    });
  },
  {
    name: 'medicine-products-routes',
    dependencies: ['auth-plugin'],
  },
);
