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

/**
 * Create a new organization
 * Phase 1: Auth & Organizations
 */
export const createOrganization = mutation({
  args: {
    name: v.string(),
    ownerId: v.string(),
    subscriptionTier: v.optional(v.string()),
    industry: v.optional(v.string()),
    sector: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const orgId = await ctx.db.insert("organizations", {
      name: args.name,
      ownerId: args.ownerId,
      industry: args.industry,
      sector: args.sector,
      subscriptionTier: args.subscriptionTier ?? "free",
      includedSeats: 1,
      purchasedSeats: 0,
      whatsappWebhookVerified: false,
      twilioWhatsappConfigured: false,
      createdAt: now,
      updatedAt: now,
    });

    // Auto-add the owner as a member with OWNER role
    await ctx.db.insert("organization_members", {
      organizationId: orgId,
      userId: args.ownerId,
      role: "OWNER",
      joinedAt: now,
    });

    return orgId;
  },
});

/**
 * Add a member to an organization
 * Phase 1: Auth & Organizations
 */
export const addMember = mutation({
  args: {
    organizationId: v.string(),
    userId: v.string(),
    role: v.optional(v.string()),
    invitedBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    // Check if already a member
    const existing = await ctx.db
      .query("organization_members")
      .withIndex("by_organizationId_userId", (q: any) =>
        q.eq("organizationId", args.organizationId).eq("userId", args.userId),
      )
      .first();

    if (existing) {
      throw new Error("User is already a member of this organization");
    }

    const memberId = await ctx.db.insert("organization_members", {
      organizationId: args.organizationId,
      userId: args.userId,
      role: (args.role as "OWNER" | "ADMIN" | "STAFF") ?? "STAFF",
      joinedAt: Date.now(),
      invitedBy: args.invitedBy,
    });
    return memberId;
  },
});

/**
 * Remove a member from an organization
 * Phase 1: Auth & Organizations
 */
export const removeMember = mutation({
  args: {
    organizationId: v.string(),
    userId: v.string(),
  },
  handler: async (ctx, args) => {
    const member = await ctx.db
      .query("organization_members")
      .withIndex("by_organizationId_userId", (q: any) =>
        q.eq("organizationId", args.organizationId).eq("userId", args.userId),
      )
      .first();

    if (!member) {
      throw new Error("Member not found in this organization");
    }

    if (member.role === "OWNER") {
      throw new Error("Cannot remove the organization owner");
    }

    await ctx.db.delete(member._id);
    return member._id;
  },
});

/**
 * Accept an invitation — mark as ACCEPTED and add user to org
 * Phase 3: Organization route wiring (WU-04)
 */
export const acceptInvitation = mutation({
  args: {
    token: v.string(),
    userId: v.string(),
    userName: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    // Find invitation by token
    const invitation = await ctx.db
      .query("organization_invitations")
      .withIndex("by_token", (q: any) => q.eq("token", args.token))
      .first();

    if (!invitation) {
      throw new Error("Invalid invitation token");
    }

    // Check expiry
    if (Date.now() > invitation.expiresAt) {
      throw new Error("Invitation has expired");
    }

    // Check status
    if (invitation.status !== "PENDING") {
      throw new Error(
        `Invitation is already ${invitation.status.toLowerCase()}`,
      );
    }

    // Mark invitation as accepted
    await ctx.db.patch(invitation._id, {
      status: "ACCEPTED" as const,
      acceptedAt: Date.now(),
    });

    // Add user to org as member
    const existing = await ctx.db
      .query("organization_members")
      .withIndex("by_organizationId_userId", (q: any) =>
        q
          .eq("organizationId", invitation.organizationId)
          .eq("userId", args.userId),
      )
      .first();

    if (!existing) {
      await ctx.db.insert("organization_members", {
        organizationId: invitation.organizationId,
        userId: args.userId,
        role: invitation.role,
        joinedAt: Date.now(),
        invitedBy: invitation.invitedBy,
      });
    }

    return {
      organizationId: invitation.organizationId,
      role: invitation.role,
    };
  },
});

/**
 * Create an organization invitation from server-side code.
 */
export const createInvitation = mutation({
  args: {
    organizationId: v.string(),
    email: v.string(),
    name: v.optional(v.string()),
    role: v.string(),
    token: v.string(),
    invitedBy: v.string(),
    expiresAt: v.number(),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db
      .query("organization_invitations")
      .withIndex("by_token", (q: any) => q.eq("token", args.token))
      .first();

    if (existing) {
      return existing._id;
    }

    const pendingForEmail = await ctx.db
      .query("organization_invitations")
      .withIndex("by_organizationId_status", (q: any) =>
        q.eq("organizationId", args.organizationId).eq("status", "PENDING"),
      )
      .filter((q: any) => q.eq(q.field("email"), args.email))
      .first();

    if (pendingForEmail) {
      throw new Error("A pending invitation already exists for this email");
    }

    return await ctx.db.insert("organization_invitations", {
      prismaId: createId(),
      organizationId: args.organizationId,
      email: args.email,
      name: args.name,
      role: args.role as "OWNER" | "ADMIN" | "STAFF",
      token: args.token,
      status: "PENDING" as const,
      expiresAt: args.expiresAt,
      sentAt: Date.now(),
      invitedBy: args.invitedBy,
    });
  },
});

/**
 * Update a member's role in an organization
 * Phase 3: Organization route wiring (WU-04)
 */
export const updateMemberRole = mutation({
  args: {
    organizationId: v.string(),
    userId: v.string(),
    newRole: v.string(),
  },
  handler: async (ctx, args) => {
    const member = await ctx.db
      .query("organization_members")
      .withIndex("by_organizationId_userId", (q: any) =>
        q.eq("organizationId", args.organizationId).eq("userId", args.userId),
      )
      .first();

    if (!member) {
      throw new Error("Member not found in this organization");
    }

    if (member.role === "OWNER") {
      throw new Error("Cannot change the owner's role");
    }

    await ctx.db.patch(member._id, {
      role: args.newRole as "OWNER" | "ADMIN" | "STAFF",
    });

    return {
      organizationId: args.organizationId,
      userId: args.userId,
      role: args.newRole,
    };
  },
});

/**
 * Generate an API integration token for a user
 * WU-16: Staff billing/waitlists/misc route wiring
 */
export const generateApiToken = mutation({
  args: {
    userId: v.string(),
    token: v.string(),
  },
  handler: async (ctx, args) => {
    // Store the token as an integration record
    const now = Date.now();
    const id = await ctx.db.insert("integrations", {
      userId: args.userId,
      type: "zapier" as const,
      status: "connected" as const,
      accessToken: args.token,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Revoke an API integration token for a user
 * WU-16: Staff billing/waitlists/misc route wiring
 */
export const revokeApiToken = mutation({
  args: {
    userId: v.string(),
  },
  handler: async (ctx, args) => {
    // Find and delete the integration token
    const integration = await ctx.db
      .query("integrations")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .filter((q) => q.eq(q.field("type"), "zapier"))
      .first();

    if (integration) {
      await ctx.db.delete(integration._id);
    }

    return { revoked: !!integration };
  },
});

/**
 * Add purchased seats to an organization
 * Phase 3: Organization route wiring (WU-04)
 */
export const addSeats = mutation({
  args: {
    organizationId: v.string(),
    quantity: v.number(),
  },
  handler: async (ctx, args) => {
    if (args.quantity < 1 || args.quantity > 20) {
      throw new Error("Quantity must be between 1 and 20");
    }

    // Find org by prismaId or _id
    let org = await ctx.db
      .query("organizations")
      .filter((q: any) => q.eq(q.field("prismaId"), args.organizationId))
      .first();

    if (!org) {
      // Try treating it as a Convex _id
      try {
        org = (await ctx.db.get(args.organizationId as any)) as typeof org;
      } catch {
        // Not a valid Convex ID
      }
    }

    if (!org) {
      throw new Error("Organization not found");
    }

    const previousSeats = org.purchasedSeats;
    const newTotal = previousSeats + args.quantity;

    await ctx.db.patch(org._id, {
      purchasedSeats: newTotal,
      updatedAt: Date.now(),
    });

    return { previousSeats, newTotal, organizationId: args.organizationId };
  },
});

/**
 * Update organization company info fields
 * Phase 3: User route wiring (WU-17)
 */
export const updateCompanyInfo = mutation({
  args: {
    organizationId: v.string(),
    name: v.optional(v.string()),
    industry: v.optional(v.string()),
    companySize: v.optional(v.string()),
    country: v.optional(v.string()),
    city: v.optional(v.string()),
    address: v.optional(v.string()),
    phoneNumber: v.optional(v.string()),
    website: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const { organizationId, ...fields } = args;

    // Try direct Convex _id first, then prismaId lookup
    let org;
    try {
      org = await ctx.db.get(organizationId as any);
    } catch {
      // Not a valid Convex ID — try prismaId
    }

    if (!org) {
      const byPrismaId = await ctx.db
        .query("organizations")
        .filter((q) => q.eq(q.field("prismaId"), organizationId))
        .first();
      org = byPrismaId;
    }

    if (!org) throw new Error("Organization not found");

    const updates: Record<string, unknown> = { updatedAt: Date.now() };
    for (const [key, val] of Object.entries(fields)) {
      if (val !== undefined) updates[key] = val;
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    await ctx.db.patch(org._id, updates as any);
    return org._id;
  },
});

/**
 * Update organization fields from server-side code.
 */
export const updateOrganizationNoAuth = mutation({
  args: {
    organizationId: v.string(),
    name: v.optional(v.string()),
    stripeCustomerId: v.optional(v.string()),
    stripeConnectAccountId: v.optional(v.string()),
    stripeConnectStatus: v.optional(v.string()),
    includedSeats: v.optional(v.number()),
    purchasedSeats: v.optional(v.number()),
    subscriptionTier: v.optional(v.string()),
    industry: v.optional(v.string()),
    sector: v.optional(v.string()),
    // WhatsApp Business API fields
    whatsappPhoneNumberId: v.optional(v.string()),
    whatsappAccessTokenEncrypted: v.optional(v.string()),
    whatsappAppSecretEncrypted: v.optional(v.string()),
    whatsappDisplayNumber: v.optional(v.string()),
    whatsappVerifyToken: v.optional(v.string()),
    whatsappWebhookVerified: v.optional(v.boolean()),
    whatsappConfiguredAt: v.optional(v.number()),
    whatsappConfiguredBy: v.optional(v.string()),
    whatsappTokenIssuedAt: v.optional(v.number()),
    // GDPR retention
    dataRetentionDays: v.optional(v.number()),
    // AI agent defaults
    aiGreetingEn: v.optional(v.string()),
    aiGreetingAr: v.optional(v.string()),
    aiModel: v.optional(v.string()),
    depositRequired: v.optional(v.boolean()),
    depositPercentage: v.optional(v.number()),
    autoSendPaymentLink: v.optional(v.boolean()),
    // Per-org WhatsApp first-message greeting
    greetingMessageEn: v.optional(v.string()),
    greetingMessageAr: v.optional(v.string()),
    autoGreetingEnabled: v.optional(v.boolean()),
    // Twilio WhatsApp credentials (for Twilio Sandbox / production)
    twilioAccountSid: v.optional(v.string()),
    twilioAuthTokenEncrypted: v.optional(v.string()),
    twilioWhatsappNumber: v.optional(v.string()),
    twilioWhatsappConfigured: v.optional(v.boolean()),
    twilioWhatsappConfiguredAt: v.optional(v.number()),
    twilioWhatsappConfiguredBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const { organizationId, ...fields } = args;

    let org: Doc<"organizations"> | null = await ctx.db
      .query("organizations")
      .filter((q: any) => q.eq(q.field("prismaId"), organizationId))
      .first();

    if (!org) {
      try {
        org = (await ctx.db.get(
          organizationId as any,
        )) as Doc<"organizations"> | null;
      } catch {
        org = null;
      }
    }

    if (!org) return null;

    const updates: Record<string, unknown> = { updatedAt: Date.now() };
    for (const [key, value] of Object.entries(fields)) {
      if (value !== undefined) {
        updates[key] = value;
      }
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    await ctx.db.patch(org._id, updates as any);
    return org._id;
  },
});
