import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import { NutritionTargetSchema, NutritionTargetResponseSchema } from '@meshitrack/shared';
import { NutritionTargetRepository } from './nutrition-target.repository.js';
import { NutritionTargetService } from './nutrition-target.service.js';

type AnyTargetDoc = {
  _id: string | { toString: () => string };
  userId: string;
  householdId: string;
  dailyCalories: number;
  proteinG: number;
  carbsG: number;
  fatG: number;
  fiberG?: number | null;
  sugarG?: number | null;
  sodiumMg?: number | null;
  isActive: boolean;
  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 toNutritionTargetResponse(
  doc: AnyTargetDoc,
): z.infer<typeof NutritionTargetResponseSchema> {
  return {
    _id: toStr(doc._id),
    userId: doc.userId,
    householdId: doc.householdId,
    dailyCalories: doc.dailyCalories,
    proteinG: doc.proteinG,
    carbsG: doc.carbsG,
    fatG: doc.fatG,
    ...(doc.fiberG != null ? { fiberG: doc.fiberG } : {}),
    ...(doc.sugarG != null ? { sugarG: doc.sugarG } : {}),
    ...(doc.sodiumMg != null ? { sodiumMg: doc.sodiumMg } : {}),
    isActive: doc.isActive,
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}

declare module '@fastify/awilix' {
  interface Cradle {
    nutritionTargetRepository: NutritionTargetRepository;
    nutritionTargetService: NutritionTargetService;
  }
}

export default fp(
  async (fastify) => {
    fastify.diContainer.register({
      nutritionTargetRepository: asClass(NutritionTargetRepository, {
        lifetime: Lifetime.SINGLETON,
      }),
      nutritionTargetService: asClass(NutritionTargetService, { lifetime: Lifetime.SINGLETON }),
    });

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

    // GET /api/v1/households/:householdId/nutrition-targets — get current active targets
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/nutrition-targets',
      schema: {
        params: paramsSchema,
        response: {
          200: z.union([
            NutritionTargetResponseSchema,
            z.object({ message: z.literal('No active targets defined') }),
          ]),
        },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('nutritionTargetService');
        const userId = request.user.keycloakId;
        const target = await service.getActiveByUser(userId, request.params.householdId);

        if (!target) {
          return reply.status(200).send({ message: 'No active targets defined' });
        }

        return reply.send(toNutritionTargetResponse(target as AnyTargetDoc));
      },
    });

    // GET /api/v1/households/:householdId/nutrition-targets/history — get all targets historically
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId/nutrition-targets/history',
      schema: {
        params: paramsSchema,
        response: {
          200: z.array(NutritionTargetResponseSchema),
        },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('nutritionTargetService');
        const userId = request.user.keycloakId;
        const targets = await service.getAllByUser(userId, request.params.householdId);
        return reply.send(targets.map((t) => toNutritionTargetResponse(t as AnyTargetDoc)));
      },
    });

    // POST /api/v1/households/:householdId/nutrition-targets — upsert active targets
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/nutrition-targets',
      schema: {
        params: paramsSchema,
        body: NutritionTargetSchema,
        response: { 201: NutritionTargetResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('nutritionTargetService');
        const userId = request.user.keycloakId;
        const target = await service.setTarget(userId, request.params.householdId, request.body);
        return reply.status(201).send(toNutritionTargetResponse(target as AnyTargetDoc));
      },
    });

    // POST /api/v1/households/:householdId/nutrition-targets/presets — generate macros via preset strategy
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/nutrition-targets/presets',
      schema: {
        params: paramsSchema,
        body: z.object({
          calories: z.number().positive(),
          strategy: z.enum(['maintenance', 'loss', 'gain']),
        }),
        response: { 200: NutritionTargetSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('nutritionTargetService');
        const calculated = service.calculatePreset(request.body.calories, request.body.strategy);
        return reply.send(calculated);
      },
    });
  },
  {
    name: 'nutrition-targets-routes',
    dependencies: ['auth-plugin'],
  },
);
