/* eslint-disable @typescript-eslint/no-explicit-any */

import { query } from "../_generated/server";
import type { Id } from "../_generated/dataModel";
import { v } from "convex/values";

async function findOrganizationByAnyId(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ctx: any,
  organizationId: string,
) {
  let org = await ctx.db
    .query("organizations")
    .filter((q: any) => q.eq(q.field("prismaId"), organizationId))
    .first();

  if (org) return org;

  try {
    org = await ctx.db.get(organizationId as Id<"organizations">);
    return org;
  } catch {
    return null;
  }
}

async function findUserByAnyId(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ctx: any,
  userId: string,
) {
  const byAppUserId = await ctx.db
    .query("users")
    .withIndex("by_appUserId", (q: any) => q.eq("appUserId", userId))
    .unique();
  if (byAppUserId) return byAppUserId;

  const byPrismaId = await ctx.db
    .query("users")
    .filter((q: any) => q.eq(q.field("prismaId"), userId))
    .first();
  if (byPrismaId) return byPrismaId;

  try {
    return await ctx.db.get(userId as Id<"users">);
  } catch {
    return null;
  }
}

/**
 * Get an organization by ID
 * Phase 1: Auth & Organizations
 */
export const getOrganization = query({
  args: { id: v.id("organizations") },
  handler: async (ctx, args) => {
    return await ctx.db.get(args.id);
  },
});

/**
 * Get organization by Stripe Connect account ID.
 */
export const getOrganizationByConnectAccountId = query({
  args: { stripeConnectAccountId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("organizations")
      .withIndex("by_stripeConnectAccountId", (q) =>
        q.eq("stripeConnectAccountId", args.stripeConnectAccountId),
      )
      .first();
  },
});

/**
 * Get organization by owner user ID (appUserId or prismaId).
 */
export const getByOwnerId = query({
  args: { ownerId: v.string() },
  handler: async (ctx, args) => {
    // Try indexed lookup first
    const byIndex = await ctx.db
      .query("organizations")
      .withIndex("by_ownerId", (q: any) => q.eq("ownerId", args.ownerId))
      .first();
    if (byIndex) return byIndex;

    // Fallback: the ownerId stored might be an appUserId — resolve the user
    // first, then look up by their Convex _id
    const user = await findUserByAnyId(ctx, args.ownerId);
    if (user) {
      const byUserId = await ctx.db
        .query("organizations")
        .withIndex("by_ownerId", (q: any) =>
          q.eq("ownerId", user._id as string),
        )
        .first();
      if (byUserId) return byUserId;

      // Also try appUserId and prismaId variants stored as ownerId
      for (const altId of [user.appUserId, user.prismaId].filter(Boolean)) {
        const byAltId = await ctx.db
          .query("organizations")
          .withIndex("by_ownerId", (q: any) => q.eq("ownerId", altId as string))
          .first();
        if (byAltId) return byAltId;
      }

      // Last resort: look up via organization_members
      const membership = await ctx.db
        .query("organization_members")
        .withIndex("by_userId", (q: any) => q.eq("userId", user._id as string))
        .first();
      if (membership) {
        const org = await ctx.db.get(membership.organizationId as any);
        if (org) return org;
      }
    }

    // Final fallback: membership lookup with the raw ownerId value
    const rawMembership = await ctx.db
      .query("organization_members")
      .withIndex("by_userId", (q: any) => q.eq("userId", args.ownerId))
      .first();
    if (rawMembership) {
      const org = await ctx.db.get(rawMembership.organizationId as any);
      if (org) return org;
    }

    return null;
  },
});

/**
 * Get organization by Convex _id or legacy prismaId.
 */
export const getOrganizationByAnyId = query({
  args: { organizationId: v.string() },
  handler: async (ctx, args) => {
    return await findOrganizationByAnyId(ctx, args.organizationId);
  },
});

/**
 * Get organization by WhatsApp phone_number_id.
 * Used by the multi-tenant WhatsApp router for message routing.
 */
export const getOrganizationByWhatsAppPhoneNumberId = query({
  args: { whatsappPhoneNumberId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("organizations")
      .withIndex("by_whatsappPhoneNumberId", (q) =>
        q.eq("whatsappPhoneNumberId", args.whatsappPhoneNumberId),
      )
      .first();
  },
});

/**
 * Get organization by Twilio WhatsApp number (e.g. "whatsapp:+14155238886").
 * Used by the Twilio webhook for multi-tenant routing.
 */
export const getOrganizationByTwilioNumber = query({
  args: { twilioWhatsappNumber: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("organizations")
      .withIndex("by_twilioWhatsappNumber", (q) =>
        q.eq("twilioWhatsappNumber", args.twilioWhatsappNumber),
      )
      .first();
  },
});

/**
 * Get members of an organization
 * Phase 1: Auth & Organizations
 */
export const getMembers = query({
  args: { organizationId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("organization_members")
      .withIndex("by_organizationId", (q) =>
        q.eq("organizationId", args.organizationId),
      )
      .collect();
  },
});

/**
 * Get pending invitations for an organization
 * Phase 1: Auth & Organizations
 */
export const getInvitations = query({
  args: {
    organizationId: v.string(),
    status: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const q = ctx.db
      .query("organization_invitations")
      .withIndex("by_organizationId_status", (q) =>
        q.eq("organizationId", args.organizationId),
      );

    if (args.status) {
      return await q
        .filter((f) => f.eq(f.field("status"), args.status))
        .collect();
    }

    return await q.collect();
  },
});

/**
 * Get an invitation by its unique token
 * Phase 3: Organization route wiring (WU-04)
 */
export const getInvitationByToken = query({
  args: { token: v.string() },
  handler: async (ctx, args) => {
    const invitation = await ctx.db
      .query("organization_invitations")
      .withIndex("by_token", (q) => q.eq("token", args.token))
      .first();

    if (!invitation) return null;

    // Fetch the organization for its name
    let orgName: string | null = null;
    const org = await ctx.db
      .query("organizations")
      .filter((q) => q.eq(q.field("prismaId"), invitation.organizationId))
      .first();
    if (org) {
      orgName = org.name;
    }

    return {
      ...invitation,
      organizationName: orgName,
    };
  },
});

/**
 * Get a member record for a user (find which org they belong to and their role)
 * Phase 3: Organization route wiring (WU-04)
 */
export const getMemberByUserId = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("organization_members")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .first();
  },
});

/**
 * Get an owner membership for a specific user (checks OWNER role)
 * Phase 3: Organization route wiring (WU-04)
 */
export const getOwnerMembership = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    const member = await ctx.db
      .query("organization_members")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .first();

    if (!member || member.role !== "OWNER") return null;

    // Fetch the organization
    const org = await ctx.db
      .query("organizations")
      .filter((q) => q.eq(q.field("prismaId"), member.organizationId))
      .first();

    if (!org) return null;

    return {
      member,
      organization: org,
    };
  },
});

/**
 * Get full organization state for a user.
 * Returns organization, detailed members, seat config, pending invitations, and the user's role.
 */
export const getOrganizationStateByUserId = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    const membership = await ctx.db
      .query("organization_members")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .first();

    if (!membership) return null;

    const organization = await findOrganizationByAnyId(
      ctx,
      membership.organizationId,
    );
    if (!organization) return null;

    const memberDocs = await ctx.db
      .query("organization_members")
      .withIndex("by_organizationId", (q) =>
        q.eq("organizationId", membership.organizationId),
      )
      .collect();

    const members = await Promise.all(
      memberDocs.map(async (member) => {
        const user = await findUserByAnyId(ctx, member.userId);
        return {
          id: member.prismaId ?? member._id,
          userId: member.userId,
          role: member.role,
          joinedAt: member.joinedAt,
          invitedBy: member.invitedBy ?? null,
          lastActiveAt: member.lastActiveAt ?? null,
          user: user
            ? {
                id: user.appUserId ?? user.prismaId ?? user._id,
                email: user.email,
                name: user.name,
                lastLoginAt: user.lastLoginAt ?? null,
              }
            : null,
        };
      }),
    );

    const pendingInvitations =
      membership.role === "OWNER" || membership.role === "ADMIN"
        ? await ctx.db
            .query("organization_invitations")
            .withIndex("by_organizationId_status", (q) =>
              q
                .eq("organizationId", membership.organizationId)
                .eq("status", "PENDING"),
            )
            .collect()
        : [];

    const seatConfig = {
      includedSeats: organization.includedSeats,
      purchasedSeats: organization.purchasedSeats,
      totalSeats: organization.includedSeats + organization.purchasedSeats,
      usedSeats: memberDocs.length,
      availableSeats:
        organization.includedSeats +
        organization.purchasedSeats -
        memberDocs.length,
    };

    return {
      organization: {
        ...organization,
        id: organization.prismaId ?? organization._id,
      },
      members,
      pendingInvitations,
      seatConfig,
      userRole: membership.role,
    };
  },
});
