import { db, eq, and, gte, lte, sql } from "@haushaltsApp/db";
import { households, member, transactions, user, invitation } from "@haushaltsApp/db/schema";
import { auth } from "@haushaltsApp/auth";
import { Hono } from "hono";
import { authMiddleware, requireAuth, type AuthVariables } from "../middleware/auth.middleware";
import { seedDefaultCategories, getCategoriesByHousehold } from "../services/category.service";
import { getOrCreateHouseholdSettings } from "../services/household-settings.service";
import { tenantMiddleware, requireHousehold, type TenantVariables } from "../middleware/tenant.middleware";

type Variables = AuthVariables & TenantVariables;

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

householdRoutes.use("/*", authMiddleware, requireAuth);

// POST /api/households/setup — called after organization.create() in onboarding
// Upserts the households row (using org ID), then seeds default categories
householdRoutes.post("/setup", tenantMiddleware, requireHousehold, async (c) => {
  const householdId = c.get("householdId") as string;
  const user = c.get("user") as { id: string; name: string };

  // Ensure the households row exists (org ID == household ID)
  await db
    .insert(households)
    .values({ id: householdId, name: user.name, ownerId: user.id })
    .onConflictDoNothing();

  const cats = await seedDefaultCategories(householdId);
  // Ensure household_settings row exists with defaults
  await getOrCreateHouseholdSettings(householdId);
  return c.json({ categories: cats }, 201);
});

// POST /api/households/repair — no x-household-id needed
// Finds the user's first organization and upserts the households row
householdRoutes.post("/repair", async (c) => {
  const user = c.get("user") as { id: string; name: string };

  // Find first organization this user belongs to
  const membership = await db.query.member.findFirst({
    where: eq(member.userId, user.id),
    with: { organization: true },
  });

  if (!membership) {
    return c.json({ error: "No organization found — create a household first" }, 404);
  }

  const org = membership.organization;

  await db
    .insert(households)
    .values({ id: org.id, name: org.name, ownerId: user.id })
    .onConflictDoNothing();

  return c.json({ householdId: org.id, name: org.name });
});

// GET /api/households/categories — list categories for current household
householdRoutes.get("/categories", tenantMiddleware, requireHousehold, async (c) => {
  const householdId = c.get("householdId") as string;
  const cats = await getCategoriesByHousehold(householdId);
  return c.json({ categories: cats });
});

// GET /api/households — list all households the user is a member of
householdRoutes.get("/", async (c) => {
  const orgs = await auth.api.listOrganizations({
    headers: c.req.raw.headers,
  });

  const householdList = (orgs ?? []).map((org: { id: string; name: string; role?: string; members?: unknown[] }) => ({
    id: org.id,
    name: org.name,
    role: org.role ?? "member",
  }));

  return c.json({ households: householdList });
});

// GET /api/households/members — list members and pending invitations
householdRoutes.get("/members", tenantMiddleware, requireHousehold, async (c) => {
  const householdId = c.get("householdId") as string;

  const members = await db
    .select({
      userId: member.userId,
      name: user.name,
      email: user.email,
      role: member.role,
    })
    .from(member)
    .innerJoin(user, eq(member.userId, user.id))
    .where(eq(member.organizationId, householdId));

  const pendingInvitations = await db
    .select({
      id: invitation.id,
      email: invitation.email,
      role: invitation.role,
      status: invitation.status,
      expiresAt: invitation.expiresAt,
      createdAt: invitation.createdAt,
    })
    .from(invitation)
    .where(
      and(
        eq(invitation.organizationId, householdId),
        eq(invitation.status, "pending"),
      ),
    );

  return c.json({ members, pendingInvitations });
});

// DELETE /api/households/invitations/:id — revoke a pending invitation
householdRoutes.delete("/invitations/:id", tenantMiddleware, requireHousehold, async (c) => {
  const householdId = c.get("householdId") as string;
  const invitationId = c.req.param("id");

  await db
    .delete(invitation)
    .where(
      and(
        eq(invitation.id, invitationId),
        eq(invitation.organizationId, householdId),
        eq(invitation.status, "pending"),
      ),
    );

  return c.json({ success: true });
});

// GET /api/households/settlement?month=YYYY-MM — monthly settlement calculation
householdRoutes.get("/settlement", tenantMiddleware, requireHousehold, async (c) => {
  const householdId = c.get("householdId") as string;
  const monthParam = c.req.query("month");

  // Parse month — default to current month
  let year: number;
  let monthNum: number;
  if (monthParam && /^\d{4}-\d{2}$/.test(monthParam)) {
    const parts = monthParam.split("-");
    year = parseInt(parts[0]!, 10);
    monthNum = parseInt(parts[1]!, 10);
  } else {
    const now = new Date();
    year = now.getFullYear();
    monthNum = now.getMonth() + 1;
  }

  const from = `${year}-${String(monthNum).padStart(2, "0")}-01`;
  const lastDay = new Date(year, monthNum, 0).getDate();
  const to = `${year}-${String(monthNum).padStart(2, "0")}-${String(lastDay).padStart(2, "0")}`;
  const month = `${year}-${String(monthNum).padStart(2, "0")}`;

  // Load all members of the household (organization) with their user names
  const members = await db
    .select({
      userId: member.userId,
      name: user.name,
    })
    .from(member)
    .innerJoin(user, eq(member.userId, user.id))
    .where(eq(member.organizationId, householdId));

  const memberCount = members.length;

  // Load all household-scope expense transactions for the month
  const expenseRows = await db
    .select({
      userId: transactions.userId,
      total: sql<string>`sum(${transactions.amount}::numeric)`,
    })
    .from(transactions)
    .where(
      and(
        eq(transactions.householdId, householdId),
        eq(transactions.scope, "household"),
        eq(transactions.type, "expense"),
        gte(transactions.date, from),
        lte(transactions.date, to),
      ),
    )
    .groupBy(transactions.userId);

  // Build paid map
  const paidByUser: Record<string, number> = {};
  let totalExpenses = 0;
  for (const row of expenseRows) {
    const amount = Number(row.total ?? 0);
    paidByUser[row.userId] = amount;
    totalExpenses += amount;
  }

  const perMember = memberCount > 0 ? totalExpenses / memberCount : 0;

  const membersResult = members.map((m) => {
    const paid = paidByUser[m.userId] ?? 0;
    const owes = perMember - paid;
    return {
      userId: m.userId,
      name: m.name,
      paid,
      owes,
    };
  });

  return c.json({
    month,
    totalExpenses,
    memberCount,
    perMember,
    members: membersResult,
  });
});
