import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
  CreateHouseholdSchema,
  UpdateHouseholdSchema,
  JoinHouseholdSchema,
  HouseholdResponseSchema,
} from '@meshitrack/shared';

type AnyHouseholdDoc = {
  _id: string | { toString: () => string };
  name: string;
  ownerUserId: string;
  members: ReadonlyArray<{
    userId: string;
    role: string;
    joinedAt: string | { toISOString: () => string };
  }>;
  inviteCode: string;
  settings?: { timezone?: string; currency?: string; language?: string } | null;
  createdAt: string | { toISOString: () => string };
  updatedAt: string | { toISOString: () => string };
};

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

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

function toHouseholdResponse(doc: AnyHouseholdDoc): z.infer<typeof HouseholdResponseSchema> {
  return {
    _id: toStr(doc._id),
    name: doc.name,
    ownerUserId: doc.ownerUserId,
    members: doc.members.map((m) => ({
      userId: m.userId,
      role: m.role,
      joinedAt: toIso(m.joinedAt),
    })),
    inviteCode: doc.inviteCode,
    settings: {
      timezone: doc.settings?.timezone ?? 'UTC',
      currency: doc.settings?.currency ?? 'USD',
      language: doc.settings?.language ?? 'en',
    },
    createdAt: toIso(doc.createdAt),
    updatedAt: toIso(doc.updatedAt),
  };
}
import { HouseholdsRepository } from './households.repository.js';
import { HouseholdsService } from './households.service.js';

declare module '@fastify/awilix' {
  interface Cradle {
    householdsRepository: HouseholdsRepository;
    householdsService: HouseholdsService;
  }
}

export default fp(
  async (fastify) => {
    // Register DI
    fastify.diContainer.register({
      householdsRepository: asClass(HouseholdsRepository, { lifetime: Lifetime.SINGLETON }),
      householdsService: asClass(HouseholdsService, { lifetime: Lifetime.SINGLETON }),
    });

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

    // POST /api/v1/households — create a new household
    app.route({
      method: 'POST',
      url: '/api/v1/households',
      config: { skipHousehold: true },
      schema: {
        body: CreateHouseholdSchema,
        response: { 201: HouseholdResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('householdsService');
        const household = await service.create(request.body, request.user.keycloakId);
        return reply.status(201).send(toHouseholdResponse(household));
      },
    });

    // GET /api/v1/households/:householdId — get household by id (members only)
    app.route({
      method: 'GET',
      url: '/api/v1/households/:householdId',
      schema: {
        params: z.object({ householdId: z.string() }),
        response: { 200: HouseholdResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('householdsService');
        const household = await service.getById(request.params.householdId);
        return reply.send(toHouseholdResponse(household));
      },
    });

    // PATCH /api/v1/households/:householdId — update household settings
    app.route({
      method: 'PATCH',
      url: '/api/v1/households/:householdId',
      schema: {
        params: z.object({ householdId: z.string() }),
        body: UpdateHouseholdSchema,
        response: { 200: HouseholdResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('householdsService');
        const household = await service.update(
          request.params.householdId,
          request.body,
          request.user.keycloakId,
        );
        return reply.send(toHouseholdResponse(household));
      },
    });

    // POST /api/v1/households/:householdId/invite — generate new invite code
    app.route({
      method: 'POST',
      url: '/api/v1/households/:householdId/invite',
      schema: {
        params: z.object({ householdId: z.string() }),
        response: { 200: HouseholdResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('householdsService');
        const household = await service.generateInviteCode(
          request.params.householdId,
          request.user.keycloakId,
        );
        return reply.send(toHouseholdResponse(household));
      },
    });

    // POST /api/v1/households/join — join via invite code
    app.route({
      method: 'POST',
      url: '/api/v1/households/join',
      config: { skipHousehold: true },
      schema: {
        body: JoinHouseholdSchema,
        response: { 200: HouseholdResponseSchema },
      },
      handler: async (request, reply) => {
        const service = fastify.diContainer.resolve('householdsService');
        const household = await service.join(request.body.inviteCode, request.user.keycloakId);
        return reply.send(toHouseholdResponse(household));
      },
    });
  },
  {
    name: 'households-routes',
    // users-routes must load first: it registers UsersRepository into the DI container,
    // which HouseholdsService depends on.
    dependencies: ['auth-plugin', 'users-routes'],
  },
);
