import { HttpError } from "@batch-cooking/error-tools";
import { wrapAsyncHandler } from "@batch-cooking/express-tools";
import {
  createHouseSchema,
  ErrorCode,
  joinHouseSchema,
  renameHouseSchema,
  updateHouseSourcesSchema,
} from "@batch-cooking/shared";
import { Router } from "express";
import { type AuthLocals, requireAuth } from "../../middlewares/require-auth.js";
import {
  createHouse,
  deleteHouse,
  getCurrentHouse,
  getHouseSourceIds,
  joinHouse,
  leaveCurrentHouse,
  removeMember,
  renameHouse,
  updateHouseSources,
} from "./house.service.js";

/** Router mounted at `/house` in app.ts. Every route requires a session — a household is per-user (via their profile), never public. */
export const houseRouter = Router();

houseRouter.get(
  "/current",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
    const house = await getCurrentHouse(res.locals.userProfile.houseId);
    res.status(200).json(house);
  }),
);

/** The household step of the profile journey (onboarding wizard and the `/parametres/foyer` settings page both call this) — renaming, open to any member. */
houseRouter.patch(
  "/current",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
    const input = renameHouseSchema.parse(req.body);
    const house = await renameHouse(res.locals.userProfile.houseId, input.name);
    res.status(200).json(house);
  }),
);

/** Creates a new household for a profile that doesn't have one yet — the "create" half of the optional household step. */
houseRouter.post(
  "/",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
    const input = createHouseSchema.parse(req.body);
    const house = await createHouse(
      res.locals.userProfile.id,
      res.locals.userProfile.houseId,
      input.name,
    );
    res.status(201).json(house);
  }),
);

/** Joins an existing household by invite code — the "join" half of the optional household step. */
houseRouter.post(
  "/join",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
    const input = joinHouseSchema.parse(req.body);
    const house = await joinHouse(
      res.locals.userProfile.id,
      res.locals.userProfile.houseId,
      input.inviteCode,
    );
    res.status(200).json(house);
  }),
);

/** Removes the caller from their own household — see `deleteHouse` below for removing the household itself. */
houseRouter.post(
  "/leave",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
    await leaveCurrentHouse(res.locals.userProfile.id, res.locals.userProfile.houseId);
    res.status(204).end();
  }),
);

/** Deletes the household entirely — every member loses it. Admin-only, see `house.service.ts`. */
houseRouter.delete(
  "/current",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
    await deleteHouse(res.locals.userProfile.id, res.locals.userProfile.houseId);
    res.status(204).end();
  }),
);

/** Which recipe sources the household currently sees in its recipe tabs — the source step of the onboarding wizard and the `/parametres/foyer` settings page both call this. */
houseRouter.get(
  "/current/sources",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
    const sourceIds = await getHouseSourceIds(res.locals.userProfile.houseId);
    res.status(200).json(sourceIds);
  }),
);

/** Replaces the household's enabled-source set — same callers as the GET above. */
houseRouter.patch(
  "/current/sources",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
    const input = updateHouseSourcesSchema.parse(req.body);
    const sourceIds = await updateHouseSources(res.locals.userProfile.houseId, input.sourceIds);
    res.status(200).json(sourceIds);
  }),
);

/** Removes one specific member from the caller's household. Admin-only, see `house.service.ts`. */
houseRouter.delete(
  "/members/:memberId",
  requireAuth,
  wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
    const memberId = Number(req.params.memberId);
    if (!Number.isInteger(memberId)) {
      throw new HttpError(400, ErrorCode.VALIDATION_ERROR, "memberId must be an integer");
    }
    const house = await removeMember(
      res.locals.userProfile.id,
      res.locals.userProfile.houseId,
      memberId,
    );
    res.status(200).json(house);
  }),
);
