import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreateRefillListSchema,
  UpdateRefillListSchema,
  UpdateRefillListItemSchema,
  RefillListQuerySchema,
  RefillAlertQuerySchema,
  RefillAlertResponseSchema,
  RefillListResponseSchema,
  RefillListListResponseSchema,
  AddToCabinetResponseSchema,
  StoreComparisonItemSchema,
} from '@meshitrack/shared';
import { RefillsRepository } from './refills.repository.js';
import { RefillsService } from './refills.service.js';

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

type AnyItem = {
  _id: string | { toString: () => string };
  medicineId: string;
  medicineName: string;
  quantity: number;
  unit: string;
  estimatedPrice?: number;
  actualPrice?: number;
  checked: boolean;
  checkedAt?: Date | string;
  addedToCabinet: boolean;
  storeId?: string;
  notes?: string;
};

type AnyRefillList = {
  _id: string | { toString: () => string };
  householdId: string;
  name: string;
  items: AnyItem[];
  status: string;
  preferredStoreId?: string;
  totalEstimatedCost?: number;
  createdBy: string;
  createdAt: Date | string | { toISOString: () => string };
  updatedAt: Date | string | { toISOString: () => string };
};

function toItemResponse(rawItem: unknown) {
  const item = rawItem as AnyItem;
  return {
    _id: typeof item._id === 'string' ? item._id : item._id.toString(),
    medicineId: item.medicineId,
    medicineName: item.medicineName,
    quantity: item.quantity,
    unit: item.unit,
    ...(item.estimatedPrice != null ? { estimatedPrice: item.estimatedPrice } : {}),
    ...(item.actualPrice != null ? { actualPrice: item.actualPrice } : {}),
    checked: item.checked,
    ...(item.checkedAt != null ? { checkedAt: toIso(item.checkedAt) } : {}),
    addedToCabinet: item.addedToCabinet,
    ...(item.storeId != null ? { storeId: item.storeId } : {}),
    ...(item.notes != null ? { notes: item.notes } : {}),
  };
}

function toListResponse(rawDoc: unknown) {
  const doc = rawDoc as AnyRefillList;
  return {
    _id: typeof doc._id === 'string' ? doc._id : doc._id.toString(),
    householdId: doc.householdId,
    name: doc.name,
    items: doc.items.map(toItemResponse),
    status: doc.status as never,
    ...(doc.preferredStoreId != null ? { preferredStoreId: doc.preferredStoreId } : {}),
    ...(doc.totalEstimatedCost != null ? { totalEstimatedCost: doc.totalEstimatedCost } : {}),
    createdBy: doc.createdBy,
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    refillsRepository: RefillsRepository;
    refillsService: RefillsService;
  }
}

export default fp(
  async (fastify) => {
    fastify.diContainer.register({
      refillsRepository: asClass(RefillsRepository, { lifetime: Lifetime.SINGLETON }),
      refillsService: asClass(RefillsService, { lifetime: Lifetime.SINGLETON }),
    });

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

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/refills/alerts',
      schema: {
        params: householdParams,
        querystring: RefillAlertQuerySchema,
        response: { 200: z.object({ data: z.array(RefillAlertResponseSchema) }) },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const alerts = await service.getAlerts(
          request.params.householdId,
          request.query.userId ?? request.user.keycloakId,
          request.query.thresholdDays,
        );
        return reply.send({
          data: alerts.map((a) => ({
            ...a,
            lastKnownPrice: a.lastKnownPrice
              ? { ...a.lastKnownPrice, date: toIso(a.lastKnownPrice.date) }
              : undefined,
            cheapestOption: a.cheapestOption
              ? { ...a.cheapestOption, date: toIso(a.cheapestOption.date) }
              : undefined,
          })),
        });
      },
    });

    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/refills/lists',
      schema: {
        params: householdParams,
        body: CreateRefillListSchema,
        response: { 201: RefillListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const list = await service.createList(
          request.body,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.status(201).send(toListResponse(list));
      },
    });

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/refills/lists',
      schema: {
        params: householdParams,
        querystring: RefillListQuerySchema,
        response: { 200: RefillListListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const result = await service.list(request.params.householdId, request.query);
        return reply.send({
          data: result.data.map(toListResponse),
          pagination: result.pagination,
        });
      },
    });

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/refills/lists/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 200: RefillListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const list = await service.getById(request.params.id, request.params.householdId);
        return reply.send(toListResponse(list));
      },
    });

    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId/refills/lists/:id',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        body: UpdateRefillListSchema,
        response: { 200: RefillListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const list = await service.updateList(
          request.params.id,
          request.params.householdId,
          request.body,
        );
        return reply.send(toListResponse(list));
      },
    });

    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId/refills/lists/:id/items/:itemId',
      schema: {
        params: householdParams.extend({ id: z.string(), itemId: z.string() }),
        body: UpdateRefillListItemSchema,
        response: { 200: RefillListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const list = await service.updateItem(
          request.params.id,
          request.params.householdId,
          request.params.itemId,
          request.body,
        );
        return reply.send(toListResponse(list));
      },
    });

    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/refills/lists/:id/add-to-cabinet',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: { 200: AddToCabinetResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const result = await service.addToCabinet(
          request.params.id,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.send(result);
      },
    });

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/refills/lists/:id/store-comparison',
      schema: {
        params: householdParams.extend({ id: z.string() }),
        response: {
          200: z.object({
            data: z.array(
              z.object({
                medicineId: z.string(),
                storeOptions: z.array(StoreComparisonItemSchema),
              }),
            ),
          }),
        },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('refillsService');
        const comparisons = await service.getStoreComparison(
          request.params.id,
          request.params.householdId,
        );
        return reply.send({
          data: comparisons.map((c) => ({
            medicineId: c.medicineId,
            storeOptions: c.storeOptions.map((opt) => ({
              ...opt,
              date: toIso(opt.date),
            })),
          })),
        });
      },
    });
  },
  {
    name: 'refills-routes',
    dependencies: ['auth-plugin'],
  },
);
