/* eslint-disable @typescript-eslint/no-explicit-any */
import { mutation } from "../_generated/server";
import type { Doc, Id } from "../_generated/dataModel";
import { v } from "convex/values";
import { createId } from "@paralleldrive/cuid2";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Ctx = any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type LegacyRecord = Record<string, any>;

async function findUser(
  ctx: Ctx,
  value?: string | null,
  email?: string | null,
): Promise<Doc<"users"> | null> {
  if (value) {
    const byAppUserId = await ctx.db
      .query("users")
      .withIndex("by_appUserId", (q: any) => q.eq("appUserId", value))
      .unique();
    if (byAppUserId) return byAppUserId;

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

    try {
      const byId = await ctx.db.get(value as Id<"users">);
      if (byId) return byId;
    } catch {
      // ignore
    }
  }

  if (email) {
    return await ctx.db
      .query("users")
      .withIndex("by_email", (q: any) => q.eq("email", email.toLowerCase()))
      .unique();
  }

  return null;
}

async function findOrganization(
  ctx: Ctx,
  value?: string | null,
): Promise<Doc<"organizations"> | null> {
  if (!value) return null;

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

  try {
    const byId = await ctx.db.get(value as Id<"organizations">);
    if (byId) return byId;
  } catch {
    // ignore
  }

  return null;
}

async function findByPrismaId(
  ctx: Ctx,
  table: string,
  prismaId?: string | null,
) {
  if (!prismaId) return null;
  return await ctx.db
    .query(table)
    .filter((q: any) => q.eq(q.field("prismaId"), prismaId))
    .first();
}

function withUpdatedAt(fields: LegacyRecord, includeUpdatedAt = true) {
  return includeUpdatedAt
    ? { ...fields, updatedAt: fields.updatedAt ?? Date.now() }
    : fields;
}

export const upsertUser = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const stableAppUserId = record.appUserId ?? record.id ?? createId();
    const email = String(record.email).toLowerCase();

    const existing = await findUser(ctx, stableAppUserId, email);
    const fields = withUpdatedAt({
      appUserId: stableAppUserId,
      prismaId: record.id ?? stableAppUserId,
      email,
      name: record.name,
      password: record.password ?? undefined,
      googleId: record.googleId ?? undefined,
      clerkId: record.clerkId ?? undefined,
      emailVerified: record.emailVerified ?? undefined,
      createdAt: record.createdAt ?? Date.now(),
      lastLoginAt: record.lastLoginAt ?? undefined,
      lastLoginIp: record.lastLoginIp ?? undefined,
      failedAttempts: record.failedAttempts ?? 0,
      lockedUntil: record.lockedUntil ?? undefined,
      address: record.address ?? undefined,
      companyName: record.companyName ?? undefined,
      companySize: record.companySize ?? undefined,
      industry: record.industry ?? undefined,
      onboardingComplete: record.onboardingComplete ?? false,
      phoneNumber: record.phoneNumber ?? undefined,
      website: record.website ?? undefined,
      country: record.country ?? undefined,
      preferredCurrency: record.preferredCurrency ?? undefined,
      city: record.city ?? undefined,
      selectedPackage: record.selectedPackage ?? undefined,
      goals: record.goals ?? undefined,
      stripeCustomerId: record.stripeCustomerId ?? undefined,
      avatar: record.avatar ?? undefined,
      isActive: record.isActive ?? true,
      isSuspended: record.isSuspended ?? false,
      lastLoginCity: record.lastLoginCity ?? undefined,
      lastLoginCountry: record.lastLoginCountry ?? undefined,
      loginCount: record.loginCount ?? 0,
      n8nWebhookUrl: record.n8nWebhookUrl ?? undefined,
      n8nWorkflowId: record.n8nWorkflowId ?? undefined,
      role: record.role ?? "CUSTOMER",
      suspendedAt: record.suspendedAt ?? undefined,
      suspendedBy: record.suspendedBy ?? undefined,
      suspendedReason: record.suspendedReason ?? undefined,
      username: record.username ?? undefined,
      whatsappConnected: record.whatsappConnected ?? false,
      whatsappConnectedAt: record.whatsappConnectedAt ?? undefined,
      whatsappNumber: record.whatsappNumber ?? undefined,
      n8nWebhookToken: record.n8nWebhookToken ?? undefined,
      onboardingStep: record.onboardingStep ?? 0,
      voiceAgentConfig: record.voiceAgentConfig ?? undefined,
      voiceAgentId: record.voiceAgentId ?? undefined,
    });

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("users", fields as any);
  },
});

export const upsertUserPreference = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const user = await findUser(ctx, record.userId);
    if (!user) throw new Error(`Missing user for preference ${record.id}`);

    const existing =
      (await findByPrismaId(ctx, "user_preferences", record.id)) ??
      (await ctx.db
        .query("user_preferences")
        .withIndex("by_userId", (q: any) =>
          q.eq("userId", user.appUserId ?? user.prismaId ?? user._id),
        )
        .unique());

    const fields = withUpdatedAt({
      prismaId: record.id,
      userId: user.appUserId ?? user.prismaId ?? user._id,
      language: record.language ?? "ar",
      timezone: record.timezone ?? "Asia/Qatar",
      dateFormat: record.dateFormat ?? "DD/MM/YYYY",
      theme: record.theme ?? "system",
      emailNotifications: record.emailNotifications ?? {},
      smsNotifications: record.smsNotifications ?? {},
      whatsappNotifications: record.whatsappNotifications ?? {},
      emailFrequency: record.emailFrequency ?? "instant",
      dashboardPreferences: record.dashboardPreferences ?? {},
      createdAt: record.createdAt ?? Date.now(),
    });

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("user_preferences", fields as any);
  },
});

export const upsertLoginHistory = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const user = await findUser(ctx, record.userId);
    if (!user) throw new Error(`Missing user for login_history ${record.id}`);

    const existing = await findByPrismaId(ctx, "login_history", record.id);
    const fields = {
      prismaId: record.id,
      userId: user.appUserId ?? user.prismaId ?? user._id,
      ipAddress: record.ipAddress,
      userAgent: record.userAgent ?? undefined,
      country: record.country ?? undefined,
      countryCode: record.countryCode ?? undefined,
      city: record.city ?? undefined,
      region: record.region ?? undefined,
      latitude: record.latitude ?? undefined,
      longitude: record.longitude ?? undefined,
      timezone: record.timezone ?? undefined,
      provider: record.provider ?? "credentials",
      success: record.success ?? true,
      failReason: record.failReason ?? undefined,
      createdAt: record.createdAt ?? Date.now(),
      logoutAt: record.logoutAt ?? undefined,
    };

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("login_history", fields);
  },
});

export const upsertUserSession = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const user = await findUser(ctx, record.userId);
    if (!user) throw new Error(`Missing user for session ${record.id}`);

    const existing =
      (await findByPrismaId(ctx, "user_sessions", record.id)) ??
      (await ctx.db
        .query("user_sessions")
        .withIndex("by_token", (q: any) => q.eq("token", record.token))
        .unique());

    const loginHistory = record.loginHistoryId
      ? await findByPrismaId(ctx, "login_history", record.loginHistoryId)
      : null;

    const fields = {
      prismaId: record.id,
      userId: user.appUserId ?? user.prismaId ?? user._id,
      token: record.token,
      deviceName: record.deviceName,
      deviceType: record.deviceType,
      ipAddress: record.ipAddress,
      location: record.location ?? undefined,
      country: record.country ?? undefined,
      city: record.city ?? undefined,
      userAgent: record.userAgent,
      lastActive: record.lastActive ?? record.createdAt ?? Date.now(),
      createdAt: record.createdAt ?? Date.now(),
      expiresAt: record.expiresAt,
      loginHistoryId:
        loginHistory?.prismaId ??
        loginHistory?._id ??
        record.loginHistoryId ??
        undefined,
    };

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("user_sessions", fields);
  },
});

export const upsertAuditLog = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const user = record.userId ? await findUser(ctx, record.userId) : null;
    const existing = await findByPrismaId(ctx, "audit_logs", record.id);
    const fields = {
      prismaId: record.id,
      userId: user?.appUserId ?? user?.prismaId ?? user?._id ?? undefined,
      type: record.type,
      action: record.action,
      success: record.success,
      ip: record.ip,
      userAgent: record.userAgent ?? undefined,
      metadata: record.metadata ?? undefined,
      createdAt: record.createdAt ?? Date.now(),
    };

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("audit_logs", fields);
  },
});

export const upsertOrganization = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const owner = await findUser(ctx, record.ownerId);
    if (!owner) throw new Error(`Missing owner for organization ${record.id}`);

    const existing = await findOrganization(ctx, record.id);
    const fields = withUpdatedAt({
      prismaId: record.id,
      name: record.name,
      ownerId: owner.appUserId ?? owner.prismaId ?? owner._id,
      stripeCustomerId: record.stripeCustomerId ?? undefined,
      includedSeats: record.includedSeats ?? 0,
      purchasedSeats: record.purchasedSeats ?? 0,
      createdAt: record.createdAt ?? Date.now(),
      stripeKeysConfiguredAt: record.stripeKeysConfiguredAt ?? undefined,
      stripeKeysConfiguredBy: record.stripeKeysConfiguredBy ?? undefined,
      stripeKeysMode: record.stripeKeysMode ?? undefined,
      stripePublishableKey: record.stripePublishableKey ?? undefined,
      stripeSecretKeyEncrypted: record.stripeSecretKeyEncrypted ?? undefined,
      stripeWebhookSecretEncrypted:
        record.stripeWebhookSecretEncrypted ?? undefined,
      subscriptionTier: record.subscriptionTier ?? "free",
      whatsappPhoneNumberId: record.whatsappPhoneNumberId ?? undefined,
      whatsappAccessTokenEncrypted:
        record.whatsappAccessTokenEncrypted ?? undefined,
      whatsappAppSecretEncrypted:
        record.whatsappAppSecretEncrypted ?? undefined,
      whatsappDisplayNumber: record.whatsappDisplayNumber ?? undefined,
      whatsappVerifyToken: record.whatsappVerifyToken ?? undefined,
      whatsappWebhookVerified: record.whatsappWebhookVerified ?? false,
      whatsappConfiguredAt: record.whatsappConfiguredAt ?? undefined,
      whatsappConfiguredBy: record.whatsappConfiguredBy ?? undefined,
      stripeConnectAccountId: record.stripeConnectAccountId ?? undefined,
      stripeConnectStatus: record.stripeConnectStatus ?? undefined,
      twilioAccountSid: record.twilioAccountSid ?? undefined,
      twilioAuthTokenEncrypted: record.twilioAuthTokenEncrypted ?? undefined,
      twilioWhatsappNumber: record.twilioWhatsappNumber ?? undefined,
      twilioWhatsappConfigured: record.twilioWhatsappConfigured ?? false,
      twilioWhatsappConfiguredAt:
        record.twilioWhatsappConfiguredAt ?? undefined,
      twilioWhatsappConfiguredBy:
        record.twilioWhatsappConfiguredBy ?? undefined,
    });

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("organizations", fields as any);
  },
});

export const upsertOrganizationMember = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const organization = await findOrganization(ctx, record.organizationId);
    const user = await findUser(ctx, record.userId);
    if (!organization || !user) {
      throw new Error(
        `Missing organization/member dependency for ${record.id}`,
      );
    }

    const userId = user.appUserId ?? user.prismaId ?? user._id;
    const organizationId = organization.prismaId ?? organization._id;
    const existing =
      (await findByPrismaId(ctx, "organization_members", record.id)) ??
      (await ctx.db
        .query("organization_members")
        .withIndex("by_organizationId_userId", (q: any) =>
          q.eq("organizationId", organizationId).eq("userId", userId),
        )
        .first());

    const fields = {
      prismaId: record.id,
      organizationId,
      userId,
      role: record.role ?? "STAFF",
      joinedAt: record.joinedAt ?? Date.now(),
      invitedBy: record.invitedBy ?? undefined,
      lastActiveAt: record.lastActiveAt ?? undefined,
    };

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("organization_members", fields);
  },
});

export const upsertOrganizationInvitation = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const organization = await findOrganization(ctx, record.organizationId);
    const inviter = await findUser(ctx, record.invitedBy);
    if (!organization || !inviter) {
      throw new Error(`Missing invitation dependency for ${record.id}`);
    }

    const existing =
      (await findByPrismaId(ctx, "organization_invitations", record.id)) ??
      (await ctx.db
        .query("organization_invitations")
        .withIndex("by_token", (q: any) => q.eq("token", record.token))
        .first());

    const fields = {
      prismaId: record.id,
      organizationId: organization.prismaId ?? organization._id,
      email: String(record.email).toLowerCase(),
      name: record.name ?? undefined,
      role: record.role ?? "STAFF",
      token: record.token,
      status: record.status ?? "PENDING",
      expiresAt: record.expiresAt,
      sentAt: record.sentAt ?? Date.now(),
      invitedBy: inviter.appUserId ?? inviter.prismaId ?? inviter._id,
      acceptedAt: record.acceptedAt ?? undefined,
    };

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("organization_invitations", fields);
  },
});

export const upsertSubscription = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const user = await findUser(ctx, record.userId);
    if (!user) throw new Error(`Missing user for subscription ${record.id}`);

    const existing =
      (await findByPrismaId(ctx, "subscriptions", record.id)) ??
      (await ctx.db
        .query("subscriptions")
        .withIndex("by_stripeSubscriptionId", (q: any) =>
          q.eq("stripeSubscriptionId", record.stripeSubscriptionId),
        )
        .first());

    const fields = withUpdatedAt({
      prismaId: record.id,
      userId: user.appUserId ?? user.prismaId ?? user._id,
      stripeCustomerId: record.stripeCustomerId,
      stripeSubscriptionId: record.stripeSubscriptionId,
      stripePriceId: record.stripePriceId,
      tierId: record.tierId,
      status: record.status,
      billingPeriod: record.billingPeriod,
      currentPeriodEnd: record.currentPeriodEnd,
      trialEndsAt: record.trialEndsAt ?? undefined,
      cancelAtPeriodEnd: record.cancelAtPeriodEnd ?? false,
      cancelledAt: record.cancelledAt ?? undefined,
      lastPaymentDate: record.lastPaymentDate ?? undefined,
      lastPaymentAttempt: record.lastPaymentAttempt ?? undefined,
      paymentFailureCount: record.paymentFailureCount ?? 0,
      createdAt: record.createdAt ?? Date.now(),
      cardBrand: record.cardBrand ?? undefined,
      cardLastFour: record.cardLastFour ?? undefined,
      paymentMethodId: record.paymentMethodId ?? undefined,
      setupFeeAmount: record.setupFeeAmount ?? undefined,
      setupFeePaid: record.setupFeePaid ?? false,
      setupFeePaidAt: record.setupFeePaidAt ?? undefined,
    });

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("subscriptions", fields as any);
  },
});

export const upsertBillingHistory = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const existing = await findByPrismaId(ctx, "billing_history", record.id);
    const fields = {
      prismaId: record.id,
      customerId: record.customerId,
      subscriptionId: record.subscriptionId ?? undefined,
      invoiceId: record.invoiceId ?? undefined,
      paymentIntentId: record.paymentIntentId ?? undefined,
      event: record.event,
      amount: record.amount,
      currency: record.currency,
      metadata: record.metadata ?? undefined,
      createdAt: record.createdAt ?? Date.now(),
    };

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("billing_history", fields);
  },
});

export const upsertPayment = mutation({
  args: { record: v.any() },
  handler: async (ctx, args) => {
    const record = args.record as LegacyRecord;
    const existing =
      (await findByPrismaId(ctx, "payments", record.id)) ??
      (await ctx.db
        .query("payments")
        .withIndex("by_stripePaymentIntentId", (q: any) =>
          q.eq("stripePaymentIntentId", record.stripePaymentIntentId),
        )
        .first());

    const fields = withUpdatedAt({
      prismaId: record.id,
      stripePaymentIntentId: record.stripePaymentIntentId,
      stripeSessionId: record.stripeSessionId ?? undefined,
      amount: record.amount,
      currency: record.currency,
      status: record.status,
      customerId: record.customerId,
      customerEmail: record.customerEmail ?? undefined,
      succeededAt: record.succeededAt ?? undefined,
      failedAt: record.failedAt ?? undefined,
      errorMessage: record.errorMessage ?? undefined,
      metadata: record.metadata ?? undefined,
      createdAt: record.createdAt ?? Date.now(),
    });

    if (existing) {
      await ctx.db.patch(existing._id, fields);
      return existing._id;
    }

    return await ctx.db.insert("payments", fields as any);
  },
});
