import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreateFreshnessRuleSchema,
  UpdateFreshnessRuleSchema,
  FreshnessRuleQuerySchema,
  FreshnessRuleResponseSchema,
  FreshnessRuleListResponseSchema,
  type FreshnessRuleSource,
  type StorageLocation,
  type ProductCategory,
} from '@meshitrack/shared';
import { FreshnessRulesRepository } from './freshness-rules.repository.js';
import { FreshnessRulesService } from './freshness-rules.service.js';

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

type AnyRuleDoc = {
  _id: string | { toString(): string };
  householdId?: string | null;
  category: string;
  storageLocation: string;
  shelfLifeDays: number;
  openedLifeDays: number;
  freezerLifeDays?: number | null;
  spoilageSignsToCheck: string[];
  tips?: string | null;
  source: 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 toRuleResponse(doc: AnyRuleDoc): z.infer<typeof FreshnessRuleResponseSchema> {
  return {
    _id: toStr(doc._id),
    ...(doc.householdId ? { householdId: doc.householdId } : {}),
    category: doc.category as ProductCategory,
    storageLocation: doc.storageLocation as StorageLocation,
    shelfLifeDays: doc.shelfLifeDays,
    openedLifeDays: doc.openedLifeDays,
    ...(doc.freezerLifeDays != null ? { freezerLifeDays: doc.freezerLifeDays } : {}),
    spoilageSignsToCheck: doc.spoilageSignsToCheck,
    ...(doc.tips ? { tips: doc.tips } : {}),
    source: doc.source as FreshnessRuleSource,
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    freshnessRulesRepository: FreshnessRulesRepository;
    freshnessRulesService: FreshnessRulesService;
  }
}

export default fp(
  async (fastify) => {
    if (!fastify.diContainer.hasRegistration('freshnessRulesRepository')) {
      fastify.diContainer.register({
        freshnessRulesRepository: asClass(FreshnessRulesRepository, {
          lifetime: Lifetime.SINGLETON,
        }),
      });
    }
    fastify.diContainer.register({
      freshnessRulesService: asClass(FreshnessRulesService, { lifetime: Lifetime.SINGLETON }),
    });

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

    // GET /freshness-rules
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/freshness-rules',
      schema: {
        params: HouseholdParams,
        querystring: FreshnessRuleQuerySchema,
        response: { 200: FreshnessRuleListResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<FreshnessRulesService>('freshnessRulesService');
        const result = await service.list(request.params.householdId, request.query);
        const mapped = {
          data: result.data.map((d) => toRuleResponse(d as unknown as AnyRuleDoc)),
          pagination: result.pagination,
        };
        return reply.send(mapped);
      },
    });

    // POST /freshness-rules
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/freshness-rules',
      schema: {
        params: HouseholdParams,
        body: CreateFreshnessRuleSchema,
        response: { 201: FreshnessRuleResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<FreshnessRulesService>('freshnessRulesService');
        const rule = await service.create(request.body, request.params.householdId);
        return reply.status(201).send(toRuleResponse(rule as unknown as AnyRuleDoc));
      },
    });

    // PATCH /freshness-rules/:id
    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId/freshness-rules/:id',
      schema: {
        params: RuleParams,
        body: UpdateFreshnessRuleSchema,
        response: { 200: FreshnessRuleResponseSchema },
      },
      handler: async (request, reply) => {
        const service = request.diScope.resolve<FreshnessRulesService>('freshnessRulesService');
        const rule = await service.update(
          request.params.id,
          request.params.householdId,
          request.body,
        );
        return reply.send(toRuleResponse(rule as unknown as AnyRuleDoc));
      },
    });

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