import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
import * as Users from "./model/users";

const addressTypeValidator = v.union(
  v.literal("shipping"),
  v.literal("billing"),
);

const addressFieldsValidator = {
  type: addressTypeValidator,
  fullName: v.optional(v.string()),
  firstName: v.string(),
  lastName: v.string(),
  phone: v.string(),
  addressLine1: v.string(),
  additionalInformation: v.optional(v.string()),
  city: v.string(),
  postalCode: v.string(),
  country: v.string(),
  isDefault: v.boolean(),
  isValidated: v.optional(v.boolean()),
};

export const list = query({
  args: {},
  handler: async (ctx) => {
    const user = await Users.getCurrentUserOrThrow(ctx);
    const addresses = await ctx.db
      .query("addresses")
      .withIndex("by_user", (q) => q.eq("userId", user._id))
      .collect();
    addresses.sort((a, b) => {
      if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1;
      return b._creationTime - a._creationTime;
    });
    return addresses;
  },
});

export const add = mutation({
  args: addressFieldsValidator,
  handler: async (ctx, args) => {
    const user = await Users.getCurrentUserOrThrow(ctx);

    const existingOfType = await ctx.db
      .query("addresses")
      .withIndex("by_user_and_type", (q) =>
        q.eq("userId", user._id).eq("type", args.type),
      )
      .collect();

    const isDefault = existingOfType.length === 0 ? true : args.isDefault;

    if (isDefault && existingOfType.length > 0) {
      for (const addr of existingOfType) {
        await ctx.db.patch(addr._id, { isDefault: false });
      }
    }

    return await ctx.db.insert("addresses", {
      userId: user._id,
      type: args.type,
      fullName: args.fullName || `${args.firstName} ${args.lastName}`,
      firstName: args.firstName,
      lastName: args.lastName,
      phone: args.phone,
      addressLine1: args.addressLine1,
      additionalInformation: args.additionalInformation,
      city: args.city,
      postalCode: args.postalCode,
      country: args.country,
      isDefault,
      isValidated: args.isValidated ?? false,
    });
  },
});

export const update = mutation({
  args: {
    id: v.id("addresses"),
    type: v.optional(addressTypeValidator),
    fullName: v.optional(v.string()),
    firstName: v.optional(v.string()),
    lastName: v.optional(v.string()),
    phone: v.optional(v.string()),
    addressLine1: v.optional(v.string()),
    additionalInformation: v.optional(v.string()),
    city: v.optional(v.string()),
    postalCode: v.optional(v.string()),
    country: v.optional(v.string()),
    isDefault: v.optional(v.boolean()),
    isValidated: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const { id, ...updates } = args;
    const address = await ctx.db.get(id);
    if (!address) throw new Error("Address not found");
    await Users.requireOwnership(ctx, address.userId);
    const patch: Record<string, unknown> = {};
    if (updates.type !== undefined) patch.type = updates.type;
    if (updates.fullName !== undefined) patch.fullName = updates.fullName;
    if (updates.firstName !== undefined) patch.firstName = updates.firstName;
    if (updates.lastName !== undefined) patch.lastName = updates.lastName;
    if (updates.phone !== undefined) patch.phone = updates.phone;
    if (updates.addressLine1 !== undefined)
      patch.addressLine1 = updates.addressLine1;
    if (updates.additionalInformation !== undefined)
      patch.additionalInformation = updates.additionalInformation;
    if (updates.city !== undefined) patch.city = updates.city;
    if (updates.postalCode !== undefined) patch.postalCode = updates.postalCode;
    if (updates.country !== undefined) patch.country = updates.country;
    if (updates.isDefault !== undefined) patch.isDefault = updates.isDefault;
    if (updates.isValidated !== undefined)
      patch.isValidated = updates.isValidated;
    if (Object.keys(patch).length === 0) return id;
    await ctx.db.patch(id, patch as any);
    return id;
  },
});

export const remove = mutation({
  args: { id: v.id("addresses") },
  handler: async (ctx, { id }) => {
    const address = await ctx.db.get(id);
    if (!address) throw new Error("Address not found");
    await Users.requireOwnership(ctx, address.userId);
    if (address.isDefault) {
      const rest = await ctx.db
        .query("addresses")
        .withIndex("by_user", (q) => q.eq("userId", address.userId))
        .filter((q) => q.neq(q.field("_id"), id))
        .collect();
      const first = rest[0];
      if (first) await ctx.db.patch(first._id, { isDefault: true });
    }
    await ctx.db.delete(id);
  },
});

export const setDefault = mutation({
  args: { id: v.id("addresses") },
  handler: async (ctx, { id }) => {
    const address = await ctx.db.get(id);
    if (!address) throw new Error("Address not found");
    await Users.requireOwnership(ctx, address.userId);
    const all = await ctx.db
      .query("addresses")
      .withIndex("by_user", (q) => q.eq("userId", address.userId))
      .collect();
    for (const addr of all) {
      await ctx.db.patch(addr._id, { isDefault: addr._id === id });
    }
  },
});

export const markValidated = mutation({
  args: {
    id: v.id("addresses"),
    isValidated: v.boolean(),
  },
  handler: async (ctx, { id, isValidated }) => {
    const address = await ctx.db.get(id);
    if (!address) throw new Error("Address not found");
    await Users.requireOwnership(ctx, address.userId);
    await ctx.db.patch(id, { isValidated });
  },
});
