import { mutation } from "../_generated/server";
import { v } from "convex/values";
import {
  propertyTypeValidator,
  listingTypeValidator,
  propertyStatusValidator,
  gccCurrencyCodeValidator,
  viewingTypeValidator,
  viewingStatusValidator,
  transactionStageValidator,
} from "../schema/real_estate";

/**
 * Create a new property listing
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const createProperty = mutation({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    titleEn: v.string(),
    titleAr: v.optional(v.string()),
    descriptionEn: v.optional(v.string()),
    descriptionAr: v.optional(v.string()),
    propertyType: propertyTypeValidator,
    listingType: listingTypeValidator,
    city: v.string(),
    district: v.optional(v.string()),
    streetAddress: v.optional(v.string()),
    buildingName: v.optional(v.string()),
    unitNumber: v.optional(v.string()),
    price: v.number(),
    currency: v.optional(gccCurrencyCodeValidator),
    bedrooms: v.optional(v.number()),
    bathrooms: v.optional(v.number()),
    builtUpArea: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("property", {
      userId: args.userId,
      organizationId: args.organizationId,
      titleEn: args.titleEn,
      titleAr: args.titleAr,
      descriptionEn: args.descriptionEn,
      descriptionAr: args.descriptionAr,
      propertyType: args.propertyType,
      listingType: args.listingType,
      status: "AVAILABLE",
      furnishing: "UNFURNISHED",
      ownership: "FREEHOLD",
      isOffPlan: false,
      bedrooms: args.bedrooms,
      bathrooms: args.bathrooms,
      builtUpArea: args.builtUpArea,
      country: "QA",
      city: args.city,
      district: args.district,
      streetAddress: args.streetAddress,
      buildingName: args.buildingName,
      unitNumber: args.unitNumber,
      price: args.price,
      currency: args.currency ?? "QAR",
      isNegotiable: true,
      amenities: [],
      highlights: [],
      viewCount: 0,
      inquiryCount: 0,
      viewingCount: 0,
      listedAt: now,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update a property listing
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const updateProperty = mutation({
  args: {
    id: v.id("property"),
    titleEn: v.optional(v.string()),
    titleAr: v.optional(v.string()),
    descriptionEn: v.optional(v.string()),
    status: v.optional(propertyStatusValidator),
    price: v.optional(v.number()),
    bedrooms: v.optional(v.number()),
    bathrooms: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Property ${args.id} not found`);
    }
    const { id, ...fields } = args;
    const patch: Record<string, unknown> = { updatedAt: Date.now() };
    for (const [key, value] of Object.entries(fields)) {
      if (value !== undefined) patch[key] = value;
    }
    await ctx.db.patch(id, patch);
    return id;
  },
});

/**
 * Delete a property listing (hard delete).
 *
 * Used by the dashboard "delete property" action and by the
 * seed-sample-properties script to wipe test data before re-seeding.
 */
export const deleteProperty = mutation({
  args: { id: v.id("property") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) return null;
    await ctx.db.delete(args.id);
    return args.id;
  },
});

/**
 * Schedule a property viewing
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const scheduleViewing = mutation({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    // Optional — WhatsApp bookings may not have a specific property
    // picked yet (the broker matches one based on the customer's
    // criteria captured in clientNotes).
    propertyId: v.optional(v.string()),
    brokerId: v.optional(v.string()),
    clientName: v.string(),
    clientPhone: v.string(),
    clientEmail: v.optional(v.string()),
    viewingType: v.optional(viewingTypeValidator),
    viewingDate: v.number(),
    viewingTime: v.string(),
    duration: v.optional(v.number()),
    source: v.optional(v.string()),
    clientNotes: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("property_viewing", {
      userId: args.userId,
      organizationId: args.organizationId,
      propertyId: args.propertyId,
      brokerId: args.brokerId,
      clientName: args.clientName,
      clientPhone: args.clientPhone,
      clientEmail: args.clientEmail,
      viewingType: args.viewingType ?? "INDIVIDUAL",
      viewingDate: args.viewingDate,
      viewingTime: args.viewingTime,
      duration: args.duration ?? 30,
      status: "REQUESTED",
      currentAttendees: 1,
      accessConfirmed: false,
      keyCollected: false,
      source: args.source ?? "manual",
      clientNotes: args.clientNotes,
      createdAt: now,
      updatedAt: now,
    });

    // Increment viewing count on the linked property (if any)
    if (args.propertyId) {
      const property = await ctx.db
        .query("property")
        .filter((q) => q.eq(q.field("_id"), args.propertyId))
        .first();
      if (property) {
        await ctx.db.patch(property._id, {
          viewingCount: property.viewingCount + 1,
          updatedAt: now,
        });
      }
    }

    return id;
  },
});

/**
 * Update viewing status (REQUESTED -> CONFIRMED -> VIEW_COMPLETED, etc.)
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const updateViewingStatus = mutation({
  args: {
    id: v.id("property_viewing"),
    status: viewingStatusValidator,
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Viewing ${args.id} not found`);
    }
    await ctx.db.patch(args.id, {
      status: args.status,
      updatedAt: Date.now(),
    });
    return args.id;
  },
});

// ─── Gap-fill mutations for WU-09/WU-10 wiring ────────────────────────────────

/**
 * Assign a broker to a property (smart assignment result stored)
 * WU-09 gap-fill
 */
export const assignBroker = mutation({
  args: {
    propertyId: v.string(),
    brokerId: v.string(),
  },
  handler: async (ctx, args) => {
    // Find the property by string field (prismaId or _id)
    const property = await ctx.db
      .query("property")
      .filter((q) => q.eq(q.field("_id"), args.propertyId))
      .first();
    if (!property) {
      throw new Error(`Property ${args.propertyId} not found`);
    }
    await ctx.db.patch(property._id, {
      listingBrokerId: args.brokerId,
      updatedAt: Date.now(),
    });
    return property._id;
  },
});

/**
 * Create a new broker profile.
 * Used by: POST /api/real-estate/brokers/import
 */
export const createBroker = mutation({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    nameEn: v.string(),
    nameAr: v.optional(v.string()),
    phone: v.string(),
    email: v.optional(v.string()),
    whatsappNumber: v.optional(v.string()),
    licenseNumber: v.optional(v.string()),
    languages: v.optional(v.any()),
    areaSpecializations: v.optional(v.any()),
    typeSpecializations: v.optional(v.any()),
    workingDays: v.optional(v.any()),
    workStartTime: v.optional(v.string()),
    workEndTime: v.optional(v.string()),
    maxViewingsPerDay: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("broker", {
      userId: args.userId,
      organizationId: args.organizationId,
      nameEn: args.nameEn,
      nameAr: args.nameAr,
      phone: args.phone,
      email: args.email,
      whatsappNumber: args.whatsappNumber,
      licenseNumber: args.licenseNumber,
      languages: args.languages ?? ["en", "ar"],
      areaSpecializations: args.areaSpecializations ?? [],
      typeSpecializations: args.typeSpecializations ?? [],
      workingDays: args.workingDays ?? [0, 1, 2, 3, 4],
      workStartTime: args.workStartTime ?? "09:00",
      workEndTime: args.workEndTime ?? "18:00",
      maxViewingsPerDay: args.maxViewingsPerDay ?? 6,
      isActive: true,
      totalViewings: 0,
      totalClosings: 0,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Create a new client requirements record
 * WU-09 gap-fill
 */
export const createClient = mutation({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    clientName: v.string(),
    clientPhone: v.string(),
    clientEmail: v.optional(v.string()),
    listingType: v.optional(v.string()),
    propertyTypes: v.optional(v.any()),
    minBedrooms: v.optional(v.number()),
    maxBedrooms: v.optional(v.number()),
    minBudget: v.optional(v.number()),
    maxBudget: v.optional(v.number()),
    currency: v.optional(v.string()),
    preferredAreas: v.optional(v.any()),
    mustHaves: v.optional(v.any()),
    dealBreakers: v.optional(v.any()),
    minArea: v.optional(v.number()),
    timeline: v.optional(v.string()),
    purpose: v.optional(v.string()),
    financingType: v.optional(v.string()),
    preApproved: v.optional(v.boolean()),
    leadScore: v.optional(v.number()),
    isActive: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("client_requirements", {
      userId: args.userId,
      organizationId: args.organizationId,
      clientName: args.clientName,
      clientPhone: args.clientPhone,
      clientEmail: args.clientEmail,
      listingType:
        args.listingType === "SALE"
          ? "SALE"
          : args.listingType === "RENT"
            ? "RENT"
            : undefined,
      propertyTypes: args.propertyTypes ?? [],
      minBedrooms: args.minBedrooms,
      maxBedrooms: args.maxBedrooms,
      minBudget: args.minBudget,
      maxBudget: args.maxBudget,
      currency:
        (args.currency as "QAR" | "SAR" | "AED" | "KWD" | "BHD" | "OMR") ??
        "QAR",
      preferredAreas: args.preferredAreas ?? [],
      mustHaves: args.mustHaves ?? [],
      dealBreakers: args.dealBreakers ?? [],
      preApproved: args.preApproved ?? false,
      timeline: args.timeline,
      purpose: args.purpose,
      financingType: args.financingType,
      leadScore: 0,
      isActive: true,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update client requirements.
 * Used by: PUT /api/real-estate/clients/[id]
 */
export const updateClient = mutation({
  args: {
    id: v.string(),
    userId: v.string(),
    clientName: v.optional(v.string()),
    clientPhone: v.optional(v.string()),
    clientEmail: v.optional(v.string()),
    listingType: v.optional(v.string()),
    propertyTypes: v.optional(v.any()),
    minBedrooms: v.optional(v.number()),
    maxBedrooms: v.optional(v.number()),
    minBudget: v.optional(v.number()),
    maxBudget: v.optional(v.number()),
    currency: v.optional(v.string()),
    preferredAreas: v.optional(v.any()),
    mustHaves: v.optional(v.any()),
    dealBreakers: v.optional(v.any()),
    minArea: v.optional(v.number()),
    timeline: v.optional(v.string()),
    purpose: v.optional(v.string()),
    financingType: v.optional(v.string()),
    preApproved: v.optional(v.boolean()),
    leadScore: v.optional(v.number()),
    isActive: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const { id, userId, ...fields } = args;

    // Find client by _id or prismaId
    const clients = await ctx.db
      .query("client_requirements")
      .withIndex("by_userId_isActive", (q) => q.eq("userId", userId))
      .collect();
    const client = clients.find((c) => c._id === id || c.prismaId === id);
    if (!client) {
      throw new Error(`Client requirements ${id} not found`);
    }

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

    await ctx.db.patch(client._id, patch);
    return await ctx.db.get(client._id);
  },
});

/**
 * Soft-delete client requirements and remove associated matches.
 * Used by: DELETE /api/real-estate/clients/[id]
 */
export const deleteClient = mutation({
  args: { id: v.string(), userId: v.string() },
  handler: async (ctx, args) => {
    const clients = await ctx.db
      .query("client_requirements")
      .withIndex("by_userId_isActive", (q) => q.eq("userId", args.userId))
      .collect();
    const client = clients.find(
      (c) => c._id === args.id || c.prismaId === args.id,
    );
    if (!client) {
      throw new Error(`Client requirements ${args.id} not found`);
    }

    // Soft-delete
    await ctx.db.patch(client._id, { isActive: false, updatedAt: Date.now() });

    // Remove associated matches
    const matches = await ctx.db
      .query("property_match")
      .withIndex("by_requirementId_matchScore", (q) =>
        q.eq("requirementId", client._id),
      )
      .collect();
    for (const match of matches) {
      await ctx.db.delete(match._id);
    }

    return { deleted: true };
  },
});

/**
 * Refresh property matches for all active client requirements.
 * Implements the same scoring algorithm as the Prisma path.
 * Used by: POST /api/real-estate/matches/refresh
 */
export const refreshMatches = mutation({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    // Fetch active client requirements
    const activeClients = await ctx.db
      .query("client_requirements")
      .withIndex("by_userId_isActive", (q) =>
        q.eq("userId", args.userId).eq("isActive", true),
      )
      .collect();

    if (activeClients.length === 0) {
      return { clientsProcessed: 0, propertiesEvaluated: 0, matchesCreated: 0 };
    }

    // Fetch available properties once
    const allProperties = await ctx.db
      .query("property")
      .withIndex("by_userId_status", (q) =>
        q.eq("userId", args.userId).eq("status", "AVAILABLE"),
      )
      .collect();

    let matchesCreated = 0;

    for (const requirements of activeClients) {
      const filteredProps = requirements.listingType
        ? allProperties.filter(
            (p) => p.listingType === requirements.listingType,
          )
        : allProperties;

      const reqTypes = (requirements.propertyTypes as string[]) || [];
      const reqAreas = (requirements.preferredAreas as string[]) || [];
      const reqMustHaves = (requirements.mustHaves as string[]) || [];

      const scored: Array<{
        propertyId: string;
        score: number;
        reasons: string[];
      }> = [];

      for (const prop of filteredProps) {
        let score = 0;
        const reasons: string[] = [];

        // Price (30%)
        if (
          requirements.minBudget !== undefined ||
          requirements.maxBudget !== undefined
        ) {
          const inRange =
            (requirements.minBudget === undefined ||
              prop.price >= requirements.minBudget) &&
            (requirements.maxBudget === undefined ||
              prop.price <= requirements.maxBudget);
          if (inRange) {
            score += 30;
            reasons.push("Price in budget");
          } else if (
            requirements.maxBudget &&
            prop.price <= requirements.maxBudget * 1.1
          ) {
            score += 15;
            reasons.push("Price slightly over budget");
          }
        } else {
          score += 15;
        }

        // Bedrooms (20%)
        if (
          prop.bedrooms !== undefined &&
          (requirements.minBedrooms !== undefined ||
            requirements.maxBedrooms !== undefined)
        ) {
          const ok =
            (requirements.minBedrooms === undefined ||
              prop.bedrooms! >= requirements.minBedrooms) &&
            (requirements.maxBedrooms === undefined ||
              prop.bedrooms! <= requirements.maxBedrooms);
          if (ok) {
            score += 20;
            reasons.push("Bedrooms match");
          }
        } else {
          score += 10;
        }

        // Area (25%)
        if (reqAreas.length > 0) {
          if (prop.district && reqAreas.includes(prop.district)) {
            score += 25;
            reasons.push(`District: ${prop.district}`);
          } else if (prop.city && reqAreas.includes(prop.city)) {
            score += 15;
            reasons.push(`City: ${prop.city}`);
          }
        } else {
          score += 12;
        }

        // Amenities (15%)
        if (reqMustHaves.length > 0) {
          const propAmenities = (prop.amenities as string[]) || [];
          const matched = reqMustHaves.filter((a: string) =>
            propAmenities.some((pa: string) =>
              pa.toLowerCase().includes(a.toLowerCase()),
            ),
          );
          score += Math.min(
            15,
            Math.round((matched.length / reqMustHaves.length) * 15),
          );
          if (matched.length > 0) {
            reasons.push(`${matched.length}/${reqMustHaves.length} must-haves`);
          }
        } else {
          score += 7;
        }

        // Type (10%)
        if (reqTypes.length > 0) {
          if (reqTypes.includes(prop.propertyType)) {
            score += 10;
            reasons.push(`Type: ${prop.propertyType}`);
          }
        } else {
          score += 5;
        }

        if (score > 0) {
          scored.push({ propertyId: prop._id, score, reasons });
        }
      }

      // Sort and keep top 50
      scored.sort((a, b) => b.score - a.score);
      const top = scored.slice(0, 50);

      for (const match of top) {
        // Check if match already exists
        const existing = await ctx.db
          .query("property_match")
          .withIndex("by_propertyId_requirementId", (q) =>
            q
              .eq("propertyId", match.propertyId)
              .eq("requirementId", requirements._id),
          )
          .first();

        if (existing) {
          await ctx.db.patch(existing._id, {
            matchScore: match.score,
            matchReasons: match.reasons,
          });
        } else {
          await ctx.db.insert("property_match", {
            propertyId: match.propertyId,
            requirementId: requirements._id,
            matchScore: match.score,
            matchReasons: match.reasons,
            status: "new",
            createdAt: Date.now(),
          });
          matchesCreated++;
        }
      }
    }

    return {
      clientsProcessed: activeClients.length,
      propertiesEvaluated: allProperties.length,
      matchesCreated,
    };
  },
});

/**
 * Create a real estate transaction.
 * Used by: POST /api/real-estate/transactions
 */
export const createTransaction = mutation({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    propertyId: v.string(),
    clientName: v.string(),
    clientPhone: v.string(),
    clientEmail: v.optional(v.string()),
    brokerId: v.optional(v.string()),
    stage: v.optional(transactionStageValidator),
    offerAmount: v.optional(v.number()),
    currency: v.optional(gccCurrencyCodeValidator),
    notes: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("real_estate_transaction", {
      userId: args.userId,
      organizationId: args.organizationId,
      propertyId: args.propertyId,
      clientName: args.clientName,
      clientPhone: args.clientPhone,
      clientEmail: args.clientEmail,
      brokerId: args.brokerId,
      stage: args.stage ?? "INQUIRY",
      offerAmount: args.offerAmount,
      currency: args.currency ?? "QAR",
      notes: args.notes,
      depositPaid: false,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update a real estate transaction (stage transitions, amounts, etc.).
 * Used by: PUT /api/real-estate/transactions/[id]
 */
export const updateTransaction = mutation({
  args: {
    id: v.string(),
    userId: v.string(),
    stage: v.optional(transactionStageValidator),
    offerAmount: v.optional(v.number()),
    counterAmount: v.optional(v.number()),
    agreedAmount: v.optional(v.number()),
    depositAmount: v.optional(v.number()),
    depositPaid: v.optional(v.boolean()),
    notes: v.optional(v.string()),
    lostReason: v.optional(v.string()),
    brokerId: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const { id, userId, ...fields } = args;

    // Find transaction by _id or prismaId
    const transactions = await ctx.db
      .query("real_estate_transaction")
      .withIndex("by_userId_stage", (q) => q.eq("userId", userId))
      .collect();
    const tx = transactions.find((t) => t._id === id || t.prismaId === id);
    if (!tx) {
      throw new Error(`Transaction ${id} not found`);
    }

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

    // Auto-set date fields on stage transitions
    if (fields.stage === "OFFER_MADE" && !tx.offerDate) {
      patch.offerDate = Date.now();
    }
    if (fields.stage === "CONTRACT_SIGNED" && !tx.contractDate) {
      patch.contractDate = Date.now();
    }
    if (fields.stage === "TX_COMPLETED" && !tx.completionDate) {
      patch.completionDate = Date.now();
    }
    if (fields.depositPaid && !tx.depositPaidAt) {
      patch.depositPaidAt = Date.now();
    }

    await ctx.db.patch(tx._id, patch);
    return await ctx.db.get(tx._id);
  },
});

/**
 * Create a tenant issue linked to a property.
 * Used by: POST /api/dashboard/tenant-issues
 */
export const createTenantIssue = mutation({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    propertyId: v.string(),
    title: v.string(),
    description: v.optional(v.string()),
    category: v.union(
      v.literal("plumbing"),
      v.literal("electrical"),
      v.literal("hvac"),
      v.literal("structural"),
      v.literal("appliance"),
      v.literal("pest_control"),
      v.literal("cleaning"),
      v.literal("other"),
    ),
    priority: v.union(
      v.literal("low"),
      v.literal("medium"),
      v.literal("high"),
      v.literal("urgent"),
    ),
    unit: v.optional(v.string()),
    tenantName: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("tenant_issue", {
      userId: args.userId,
      organizationId: args.organizationId,
      propertyId: args.propertyId,
      title: args.title,
      description: args.description,
      category: args.category,
      priority: args.priority,
      status: "open",
      unit: args.unit,
      tenantName: args.tenantName,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update a tenant issue (status, priority, etc.).
 * Used by: PUT /api/dashboard/tenant-issues
 */
export const updateTenantIssue = mutation({
  args: {
    id: v.string(),
    userId: v.string(),
    status: v.optional(
      v.union(
        v.literal("open"),
        v.literal("in_progress"),
        v.literal("resolved"),
        v.literal("closed"),
      ),
    ),
    priority: v.optional(
      v.union(
        v.literal("low"),
        v.literal("medium"),
        v.literal("high"),
        v.literal("urgent"),
      ),
    ),
    title: v.optional(v.string()),
    description: v.optional(v.string()),
    category: v.optional(
      v.union(
        v.literal("plumbing"),
        v.literal("electrical"),
        v.literal("hvac"),
        v.literal("structural"),
        v.literal("appliance"),
        v.literal("pest_control"),
        v.literal("cleaning"),
        v.literal("other"),
      ),
    ),
    unit: v.optional(v.string()),
    tenantName: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const { id, userId, ...fields } = args;

    const issues = await ctx.db
      .query("tenant_issue")
      .withIndex("by_userId_status", (q) => q.eq("userId", userId))
      .collect();
    const issue = issues.find((i) => i._id === id);
    if (!issue) {
      throw new Error(`Tenant issue ${id} not found`);
    }

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

    await ctx.db.patch(issue._id, patch);
    return await ctx.db.get(issue._id);
  },
});

/**
 * Update broker details
 * WU-09 gap-fill
 */
export const updateBroker = mutation({
  args: {
    id: v.id("broker"),
    nameEn: v.optional(v.string()),
    nameAr: v.optional(v.string()),
    phone: v.optional(v.string()),
    email: v.optional(v.string()),
    whatsappNumber: v.optional(v.string()),
    photoUrl: v.optional(v.string()),
    bioEn: v.optional(v.string()),
    bioAr: v.optional(v.string()),
    licenseNumber: v.optional(v.string()),
    languages: v.optional(v.any()),
    areaSpecializations: v.optional(v.any()),
    typeSpecializations: v.optional(v.any()),
    workingDays: v.optional(v.any()),
    workStartTime: v.optional(v.string()),
    workEndTime: v.optional(v.string()),
    maxViewingsPerDay: v.optional(v.number()),
    isActive: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Broker ${args.id} not found`);
    }
    const { id, ...fields } = args;
    const patch: Record<string, unknown> = { updatedAt: Date.now() };
    for (const [key, value] of Object.entries(fields)) {
      if (value !== undefined) patch[key] = value;
    }
    await ctx.db.patch(id, patch);
    return id;
  },
});

/**
 * Submit viewing feedback with side effects on the viewing record.
 * Used by: POST /api/real-estate/viewings/[id]/feedback
 */
export const submitViewingFeedback = mutation({
  args: {
    viewingId: v.string(),
    userId: v.string(),
    overallRating: v.optional(v.number()),
    priceOpinion: v.optional(v.string()),
    sizeOpinion: v.optional(v.string()),
    locationOpinion: v.optional(v.string()),
    conditionNote: v.optional(v.string()),
    wouldRecommend: v.optional(v.boolean()),
    brokerNotes: v.optional(v.string()),
    interestLevel: v.optional(v.string()),
    suggestedAction: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const { viewingId, userId, ...feedbackFields } = args;

    // Find viewing by _id or prismaId
    const viewings = await ctx.db
      .query("property_viewing")
      .withIndex("by_userId_viewingDate", (q) => q.eq("userId", userId))
      .collect();
    const viewing = viewings.find(
      (v) => v._id === viewingId || v.prismaId === viewingId,
    );
    if (!viewing) {
      throw new Error(`Viewing ${viewingId} not found`);
    }

    // Create feedback record
    const feedbackId = await ctx.db.insert("viewing_feedback", {
      viewingId: viewing._id,
      ...feedbackFields,
      createdAt: Date.now(),
    });

    // Update viewing with interest level and mark as completed
    const viewingPatch: Record<string, unknown> = { updatedAt: Date.now() };
    if (feedbackFields.interestLevel) {
      viewingPatch.interestLevel = feedbackFields.interestLevel;
    }
    if (viewing.status === "CONFIRMED" || viewing.status === "IN_PROGRESS") {
      viewingPatch.status = "VIEW_COMPLETED";
    }
    await ctx.db.patch(viewing._id, viewingPatch);

    return feedbackId;
  },
});

/**
 * Store an image reference for a property
 * WU-09 gap-fill
 */
export const uploadPropertyImage = mutation({
  args: {
    propertyId: v.string(),
    url: v.string(),
    caption: v.optional(v.string()),
    captionAr: v.optional(v.string()),
    type: v.optional(v.string()),
    sortOrder: v.optional(v.number()),
    isPrimary: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const isPrimary = args.isPrimary ?? false;

    // If setting as primary, unset existing primaries
    if (isPrimary) {
      const existingPrimaries = await ctx.db
        .query("property_image")
        .withIndex("by_propertyId_sortOrder", (q) =>
          q.eq("propertyId", args.propertyId),
        )
        .filter((f) => f.eq(f.field("isPrimary"), true))
        .collect();

      for (const img of existingPrimaries) {
        await ctx.db.patch(img._id, { isPrimary: false });
      }
    }

    // Auto-calculate sortOrder if not provided
    let effectiveSortOrder = args.sortOrder;
    if (effectiveSortOrder == null) {
      const existingImages = await ctx.db
        .query("property_image")
        .withIndex("by_propertyId_sortOrder", (q) =>
          q.eq("propertyId", args.propertyId),
        )
        .collect();
      const maxOrder = existingImages.reduce(
        (max, img) => Math.max(max, img.sortOrder),
        -1,
      );
      effectiveSortOrder = maxOrder + 1;
    }

    const id = await ctx.db.insert("property_image", {
      propertyId: args.propertyId,
      url: args.url,
      caption: args.caption,
      captionAr: args.captionAr,
      type: args.type ?? "photo",
      sortOrder: effectiveSortOrder,
      isPrimary,
      createdAt: Date.now(),
    });
    return id;
  },
});
