import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreatePriceRecordSchema,
  BulkPriceRecordInputSchema,
  PriceHistoryQuerySchema,
  PriceRecordResponseSchema,
  PriceHistoryResponseSchema,
  FoodStoreComparisonResponseSchema,
  FoodSpendingAnalyticsResponseSchema,
} from '@meshitrack/shared';
import { PricesRepository } from './prices.repository.js';
import { PricesService } from './prices.service.js';
import { ProductsRepository } from '../products/products.repository.js';
import { StoresRepository } from '../stores/stores.repository.js';

type AnyPriceDoc = {
  _id: string | { toString: () => string };
  householdId: string;
  productId: string;
  productName: string;
  storeId: string;
  storeName: string;
  price: number;
  currency: string;
  quantity: number;
  unit: string;
  pricePerUnit: number;
  date: Date | string | { toISOString: () => string };
  receiptImageUrl?: string;
  notes?: string;
  createdBy: string;
  createdAt: Date | string | { toISOString: () => string };
};

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

function toPriceRecordResponse(rawDoc: unknown) {
  const doc = rawDoc as AnyPriceDoc;
  return {
    _id: typeof doc._id === 'string' ? doc._id : doc._id.toString(),
    householdId: doc.householdId,
    productId: doc.productId,
    productName: doc.productName,
    storeId: doc.storeId,
    storeName: doc.storeName,
    price: doc.price,
    currency: doc.currency,
    quantity: doc.quantity,
    unit: doc.unit as import('@meshitrack/shared').ServingUnit,
    pricePerUnit: doc.pricePerUnit,
    date: toIso(doc.date),
    ...(doc.receiptImageUrl ? { receiptImageUrl: doc.receiptImageUrl } : {}),
    ...(doc.notes != null ? { notes: doc.notes } : {}),
    createdBy: doc.createdBy,
    createdAt: toIso(doc.createdAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    pricesRepository: PricesRepository;
    pricesService: PricesService;
  }
}

export default fp(
  async (fastify) => {
    // Register DI containers
    fastify.diContainer.register({
      pricesRepository: asClass(PricesRepository, { lifetime: Lifetime.SINGLETON }),
      pricesService: asClass(PricesService, { lifetime: Lifetime.SINGLETON }),
      productsRepository: asClass(ProductsRepository, { lifetime: Lifetime.SINGLETON }),
      storesRepository: asClass(StoresRepository, { lifetime: Lifetime.SINGLETON }),
    });

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

    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/prices',
      schema: {
        params: householdParams,
        body: CreatePriceRecordSchema,
        response: { 201: PriceRecordResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('pricesService');
        const record = await service.recordPrice(
          request.body,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.status(201).send(toPriceRecordResponse(record));
      },
    });

    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/prices/bulk',
      schema: {
        params: householdParams,
        body: BulkPriceRecordInputSchema,
        response: { 201: z.array(PriceRecordResponseSchema) },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('pricesService');
        const records = await service.recordBulkPrices(
          request.body,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.status(201).send(records.map(toPriceRecordResponse));
      },
    });

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/prices/history/:productId',
      schema: {
        params: householdParams.extend({ productId: z.string() }),
        querystring: PriceHistoryQuerySchema,
        response: { 200: PriceHistoryResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('pricesService');
        const result = await service.getPriceHistory(
          request.params.productId,
          request.params.householdId,
          request.query,
        );
        return reply.send({
          data: result.data.map(toPriceRecordResponse),
          pagination: result.pagination,
        });
      },
    });

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/prices/compare/:productId',
      schema: {
        params: householdParams.extend({ productId: z.string() }),
        response: { 200: FoodStoreComparisonResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('pricesService');
        const results = await service.compareStores(
          request.params.productId,
          request.params.householdId,
        );
        return reply.send({
          data: results.map((r) => ({
            ...r,
            date: toIso(r.date),
          })),
        });
      },
    });

    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/prices/analytics',
      schema: {
        params: householdParams,
        response: { 200: FoodSpendingAnalyticsResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('pricesService');
        const analytics = await service.getAnalytics(request.params.householdId);
        return reply.send({
          ...analytics,
          priceAlerts: analytics.priceAlerts.map((a) => ({ ...a, date: toIso(a.date) })),
        });
      },
    });
  },
  {
    name: 'prices-routes',
    dependencies: ['auth-plugin'],
  },
);
