import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreatePantryItemSchema,
  UpdatePantryItemSchema,
  TransitionPantryItemSchema,
  BatchTransitionSchema,
  PantryQuerySchema,
  ExpiringQuerySchema,
  WasteStatsQuerySchema,
  PantryItemResponseSchema,
  PantryItemListResponseSchema,
  WasteStatsResponseSchema,
  BatchTransitionResponseSchema,
  type ItemStatus,
  type FreshnessUrgency,
  type FreshnessSource,
} from '@meshitrack/shared';
import type { StorageLocation } from '@meshitrack/shared';
import { PantryRepository } from './pantry.repository.js';
import { PantryService } from './pantry.service.js';
import { ProductsRepository } from '../products/products.repository.js';
import { FreshnessRulesRepository } from '../freshness-rules/freshness-rules.repository.js';

const HouseholdParams = z.object({ householdId: z.string() });
const ItemParams = z.object({ householdId: z.string(), id: z.string() });

type AnyPantryDoc = {
  _id: string | { toString(): string };
  householdId: string;
  productId: string;
  productName: string;
  storageLocation: string;
  quantity: number;
  unit: string;
  purchaseDate: string | Date;
  expirationDate?: string | Date | null;
  openedDate?: string | Date | null;
  preparedDate?: string | Date | null;
  status: string;
  freshnessEstimate: {
    estimatedExpiryDate: string | Date;
    daysRemaining: number;
    urgency: string;
    source: string;
  };
  notes?: string | null;
  purchasePrice?: number | null;
  storeId?: string | null;
  createdBy: string;
  createdAt: string | Date;
  updatedAt: string | Date;
};

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

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

function toPantryResponse(doc: AnyPantryDoc): z.infer<typeof PantryItemResponseSchema> {
  return {
    _id: toStr(doc._id),
    householdId: doc.householdId,
    productId: doc.productId,
    productName: doc.productName,
    storageLocation: doc.storageLocation as StorageLocation,
    quantity: doc.quantity,
    unit: doc.unit,
    purchaseDate: toIso(doc.purchaseDate),
    ...(doc.expirationDate ? { expirationDate: toIso(doc.expirationDate) } : {}),
    ...(doc.openedDate ? { openedDate: toIso(doc.openedDate) } : {}),
    ...(doc.preparedDate ? { preparedDate: toIso(doc.preparedDate) } : {}),
    status: doc.status as ItemStatus,
    freshnessEstimate: {
      estimatedExpiryDate: toIso(doc.freshnessEstimate.estimatedExpiryDate),
      daysRemaining: doc.freshnessEstimate.daysRemaining,
      urgency: doc.freshnessEstimate.urgency as FreshnessUrgency,
      source: doc.freshnessEstimate.source as FreshnessSource,
    },
    ...(doc.notes ? { notes: doc.notes } : {}),
    ...(doc.purchasePrice != null ? { purchasePrice: doc.purchasePrice } : {}),
    ...(doc.storeId ? { storeId: doc.storeId } : {}),
    createdBy: doc.createdBy,
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    pantryRepository: PantryRepository;
    pantryService: PantryService;
  }
}

export default fp(
  async (fastify) => {
    // Only register if not already registered (products repo may be registered by recipes)
    if (!fastify.diContainer.hasRegistration('productsRepository')) {
      fastify.diContainer.register({
        productsRepository: asClass(ProductsRepository, { lifetime: Lifetime.SINGLETON }),
      });
    }
    if (!fastify.diContainer.hasRegistration('freshnessRulesRepository')) {
      fastify.diContainer.register({
        freshnessRulesRepository: asClass(FreshnessRulesRepository, {
          lifetime: Lifetime.SINGLETON,
        }),
      });
    }
    fastify.diContainer.register({
      pantryRepository: asClass(PantryRepository, { lifetime: Lifetime.SINGLETON }),
      pantryService: asClass(PantryService, { lifetime: Lifetime.SINGLETON }),
    });

    const app = fastify.withTypeProvider<ZodTypeProvider>();

    // GET /pantry - list pantry items
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/pantry',
      schema: {
        params: HouseholdParams,
        querystring: PantryQuerySchema,
        response: { 200: PantryItemListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const result = await service.list(request.params.householdId, request.query);
        const mapped = {
          data: result.data.map((d) => toPantryResponse(d as unknown as AnyPantryDoc)),
          pagination: result.pagination,
        };
        return reply.send(mapped);
      },
    });

    // GET /pantry/expiring-soon
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/pantry/expiring-soon',
      schema: {
        params: HouseholdParams,
        querystring: ExpiringQuerySchema,
        response: { 200: PantryItemListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const result = await service.getExpiringSoon(request.params.householdId, request.query);
        const mapped = {
          data: result.data.map((d) => toPantryResponse(d as unknown as AnyPantryDoc)),
          pagination: result.pagination,
        };
        return reply.send(mapped);
      },
    });

    // GET /pantry/stats
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/pantry/stats',
      schema: {
        params: HouseholdParams,
        querystring: WasteStatsQuerySchema,
        response: { 200: WasteStatsResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const result = await service.getWasteStats(request.params.householdId, request.query);
        return reply.send(result);
      },
    });

    // GET /pantry/:id
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/pantry/:id',
      schema: {
        params: ItemParams,
        response: { 200: PantryItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const item = await service.getById(request.params.id, request.params.householdId);
        return reply.send(toPantryResponse(item as unknown as AnyPantryDoc));
      },
    });

    // POST /pantry
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/pantry',
      schema: {
        params: HouseholdParams,
        body: CreatePantryItemSchema,
        response: { 201: PantryItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const item = await service.create(
          request.body,
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.status(201).send(toPantryResponse(item as unknown as AnyPantryDoc));
      },
    });

    // PATCH /pantry/:id
    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId/pantry/:id',
      schema: {
        params: ItemParams,
        body: UpdatePantryItemSchema,
        response: { 200: PantryItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const item = await service.update(
          request.params.id,
          request.params.householdId,
          request.body,
        );
        return reply.send(toPantryResponse(item as unknown as AnyPantryDoc));
      },
    });

    // POST /pantry/:id/transition
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/pantry/:id/transition',
      schema: {
        params: ItemParams,
        body: TransitionPantryItemSchema,
        response: { 200: PantryItemResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const item = await service.transition(
          request.params.id,
          request.params.householdId,
          request.body,
        );
        return reply.send(toPantryResponse(item as unknown as AnyPantryDoc));
      },
    });

    // POST /pantry/batch-transition
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/pantry/batch-transition',
      schema: {
        params: HouseholdParams,
        body: BatchTransitionSchema,
        response: { 200: BatchTransitionResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        const result = await service.batchTransition(request.params.householdId, request.body);
        return reply.send(result);
      },
    });

    // DELETE /pantry/:id
    app.route({
      method: 'DELETE',
      url: '/api/v1/households/:householdId/pantry/:id',
      schema: {
        params: ItemParams,
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<PantryService>('pantryService');
        await service.delete(request.params.id, request.params.householdId);
        return reply.status(204).send();
      },
    });
  },
  { name: 'pantry-routes' },
);
