import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { authMiddleware, requireAuth, type AuthVariables } from "../middleware/auth.middleware";
import { requireHousehold, tenantMiddleware, type TenantVariables } from "../middleware/tenant.middleware";
import {
  getTrips,
  createTrip,
  updateTrip,
  deleteTrip,
  completeTrip,
  getTripExpenses,
  createTripExpense,
  updateTripExpense,
  deleteTripExpense,
  getTripSummary,
  getTripSettlementPreview,
} from "../services/trips.service";
import {
  createTripSchema,
  updateTripSchema,
  createTripExpenseSchema,
  updateTripExpenseSchema,
} from "@haushaltsApp/shared/schemas/trips";

type Variables = AuthVariables & TenantVariables;

export const tripsRoutes = new Hono<{ Variables: Variables }>();

tripsRoutes.use("/*", authMiddleware, requireAuth, tenantMiddleware, requireHousehold);

// GET / — list all trips
tripsRoutes.get("/", async (c) => {
  const householdId = c.get("householdId") as string;
  const data = await getTrips(householdId);
  return c.json({ trips: data });
});

// POST / — create a trip
tripsRoutes.post(
  "/",
  zValidator("json", createTripSchema),
  async (c) => {
    const householdId = c.get("householdId") as string;
    const input = c.req.valid("json");
    const trip = await createTrip(householdId, input);
    return c.json({ trip }, 201);
  },
);

// PATCH /:id — update a trip
tripsRoutes.patch(
  "/:id",
  zValidator("json", updateTripSchema),
  async (c) => {
    const householdId = c.get("householdId") as string;
    const { id } = c.req.param();
    const input = c.req.valid("json");
    const trip = await updateTrip(id, householdId, input);
    if (!trip) return c.json({ error: "Not found" }, 404);
    return c.json({ trip });
  },
);

// DELETE /:id — delete a trip (fails if it has expenses)
tripsRoutes.delete("/:id", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id } = c.req.param();
  try {
    const deleted = await deleteTrip(id, householdId);
    if (!deleted) return c.json({ error: "Not found" }, 404);
    return c.json({ success: true });
  } catch (err) {
    if (err instanceof Error && err.message === "Has expenses") {
      return c.json({ error: "Cannot delete a trip that has expenses" }, 400);
    }
    throw err;
  }
});

// POST /:id/complete — mark a trip as completed
tripsRoutes.post("/:id/complete", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id } = c.req.param();
  const trip = await completeTrip(id, householdId);
  if (!trip) return c.json({ error: "Not found" }, 404);
  return c.json({ trip });
});

// GET /:id/expenses — list expenses for a trip
tripsRoutes.get("/:id/expenses", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id } = c.req.param();
  const expenses = await getTripExpenses(id, householdId);
  return c.json({ expenses });
});

// POST /:id/expenses — add an expense to a trip
tripsRoutes.post(
  "/:id/expenses",
  zValidator("json", createTripExpenseSchema),
  async (c) => {
    const householdId = c.get("householdId") as string;
    const { id } = c.req.param();
    const input = c.req.valid("json");
    try {
      const expense = await createTripExpense(id, householdId, input);
      return c.json({ expense }, 201);
    } catch (err) {
      if (err instanceof Error && err.message === "Trip not found") {
        return c.json({ error: "Not found" }, 404);
      }
      throw err;
    }
  },
);

// PATCH /:id/expenses/:eid — update a trip expense
tripsRoutes.patch(
  "/:id/expenses/:eid",
  zValidator("json", updateTripExpenseSchema),
  async (c) => {
    const householdId = c.get("householdId") as string;
    const { id, eid } = c.req.param();
    const input = c.req.valid("json");
    const expense = await updateTripExpense(eid, id, householdId, input);
    if (!expense) return c.json({ error: "Not found" }, 404);
    return c.json({ expense });
  },
);

// DELETE /:id/expenses/:eid — delete a trip expense
tripsRoutes.delete("/:id/expenses/:eid", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id, eid } = c.req.param();
  const deleted = await deleteTripExpense(eid, id, householdId);
  if (!deleted) return c.json({ error: "Not found" }, 404);
  return c.json({ success: true });
});

// GET /:id/settlement — preview settlement for a trip
tripsRoutes.get("/:id/settlement", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id } = c.req.param();
  const settlement = await getTripSettlementPreview(id, householdId);
  if (!settlement) return c.json({ error: "Not found" }, 404);
  return c.json({ settlement });
});

// GET /:id/summary — trip summary with spending by category
tripsRoutes.get("/:id/summary", async (c) => {
  const householdId = c.get("householdId") as string;
  const { id } = c.req.param();
  const summary = await getTripSummary(id, householdId);
  if (!summary) return c.json({ error: "Not found" }, 404);
  return c.json({ summary });
});
