import { TRPCError } from "@trpc/server";
import { eq, and } from "drizzle-orm";
import { db } from "~/server/db";
import { users } from "~/server/db/schema/auth";
import { familyGroups, familyGroupMembers } from "~/server/db/schema/subscription";
import { invitations } from "~/server/db/schema/invitation";

export async function getFamilyGroup(userId: string) {
  const membership = await db.query.familyGroupMembers.findFirst({
    where: eq(familyGroupMembers.userId, userId),
    with: {
      group: {
        with: {
          members: {
            with: {
              user: true,
            },
          },
          owner: true,
        },
      },
    },
  });

  if (!membership) {
    throw new TRPCError({ code: "NOT_FOUND", message: "No family group found" });
  }

  return membership.group;
}

export async function createFamilyGroup(ownerId: string, name: string) {
  const [group] = await db
    .insert(familyGroups)
    .values({ name, ownerId })
    .returning();

  await db.insert(familyGroupMembers).values({
    groupId: group.id,
    userId: ownerId,
    role: "owner",
  });

  return group;
}

export async function getFamilyGroupWithMembers(groupId: string) {
  const group = await db.query.familyGroups.findFirst({
    where: eq(familyGroups.id, groupId),
    with: {
      members: {
        with: {
          user: true,
        },
      },
      owner: true,
    },
  });

  if (!group) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Family group not found" });
  }

  return group;
}

export async function inviteMember(
  groupId: string,
  email: string,
  invitedBy: string,
  role: "admin" | "member" = "member",
) {
  const existing = await db.query.invitations.findFirst({
    where: and(
      eq(invitations.groupId, groupId),
      eq(invitations.email, email),
      eq(invitations.status, "pending"),
    ),
  });

  if (existing) {
    throw new TRPCError({
      code: "CONFLICT",
      message: "An active invitation already exists for this email",
    });
  }

  const memberUser = await db.query.users.findFirst({
    where: eq(users.email, email),
  });

  if (memberUser) {
    const existingMember = await db.query.familyGroupMembers.findFirst({
      where: and(
        eq(familyGroupMembers.groupId, groupId),
        eq(familyGroupMembers.userId, memberUser.id),
      ),
    });

    if (existingMember) {
      throw new TRPCError({
        code: "CONFLICT",
        message: "User is already a member of this family group",
      });
    }
  }

  const expiresAt = new Date();
  expiresAt.setDate(expiresAt.getDate() + 7);

  const [invitation] = await db
    .insert(invitations)
    .values({
      groupId,
      email,
      role,
      invitedBy,
      expiresAt,
    })
    .returning();

  return invitation;
}

export async function removeMember(groupId: string, userId: string, callerId: string) {
  const callerMember = await db.query.familyGroupMembers.findFirst({
    where: and(
      eq(familyGroupMembers.groupId, groupId),
      eq(familyGroupMembers.userId, callerId),
    ),
  });

  if (!callerMember || (callerMember.role !== "owner" && callerMember.role !== "admin")) {
    throw new TRPCError({
      code: "FORBIDDEN",
      message: "Only owner or admin can remove members",
    });
  }

  const targetMember = await db.query.familyGroupMembers.findFirst({
    where: and(
      eq(familyGroupMembers.groupId, groupId),
      eq(familyGroupMembers.userId, userId),
    ),
  });

  if (!targetMember) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Member not found" });
  }

  if (targetMember.role === "owner") {
    throw new TRPCError({
      code: "FORBIDDEN",
      message: "Cannot remove the group owner",
    });
  }

  await db
    .delete(familyGroupMembers)
    .where(eq(familyGroupMembers.id, targetMember.id));
}

export async function updateMemberRole(
  groupId: string,
  userId: string,
  role: "owner" | "admin" | "member",
  callerId: string,
) {
  const callerMember = await db.query.familyGroupMembers.findFirst({
    where: and(
      eq(familyGroupMembers.groupId, groupId),
      eq(familyGroupMembers.userId, callerId),
    ),
  });

  if (!callerMember || callerMember.role !== "owner") {
    throw new TRPCError({
      code: "FORBIDDEN",
      message: "Only the group owner can update roles",
    });
  }

  const targetMember = await db.query.familyGroupMembers.findFirst({
    where: and(
      eq(familyGroupMembers.groupId, groupId),
      eq(familyGroupMembers.userId, userId),
    ),
  });

  if (!targetMember) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Member not found" });
  }

  const [updated] = await db
    .update(familyGroupMembers)
    .set({ role })
    .where(eq(familyGroupMembers.id, targetMember.id))
    .returning();

  return updated;
}

export async function transferOwnership(groupId: string, newOwnerId: string, callerId: string) {
  const group = await db.query.familyGroups.findFirst({
    where: eq(familyGroups.id, groupId),
  });

  if (!group) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Family group not found" });
  }

  if (group.ownerId !== callerId) {
    throw new TRPCError({
      code: "FORBIDDEN",
      message: "Only the current owner can transfer ownership",
    });
  }

  const newOwnerMember = await db.query.familyGroupMembers.findFirst({
    where: and(
      eq(familyGroupMembers.groupId, groupId),
      eq(familyGroupMembers.userId, newOwnerId),
    ),
  });

  if (!newOwnerMember) {
    throw new TRPCError({ code: "NOT_FOUND", message: "New owner is not a member of the group" });
  }

  await db.transaction(async (tx) => {
    await tx
      .update(familyGroupMembers)
      .set({ role: "admin" })
      .where(and(
        eq(familyGroupMembers.groupId, groupId),
        eq(familyGroupMembers.userId, callerId),
      ));

    await tx
      .update(familyGroupMembers)
      .set({ role: "owner" })
      .where(eq(familyGroupMembers.id, newOwnerMember.id));

    await tx
      .update(familyGroups)
      .set({ ownerId: newOwnerId })
      .where(eq(familyGroups.id, groupId));
  });
}
