import { createClient } from "@/lib/supabase/server";
import { AuthenticatedUser } from "@/types/auth";
import { getPropertyById } from "./property.service";
import { PROPERTIES_LIST } from "@/lib/mock-data";
import { getCachedJson, setCachedJson, CACHE_TTL } from "@/lib/redis";

/**
 * Toggle saving a property for the authenticated user
 */
export async function toggleSavedProperty(
  actor: AuthenticatedUser,
  propertyId: string
): Promise<{ success: boolean; isSaved: boolean; error?: string }> {
  try {
    const cacheKey = `user:favorites:${actor.id}`;
    let cachedIds = (await getCachedJson<string[]>(cacheKey)) || [];
    let isCurrentlySaved = cachedIds.includes(propertyId);

    let dbSuccess = false;
    try {
      const supabase = await createClient();

      // Check if already saved in Supabase
      const { data: existing } = await supabase
        .from("saved_properties")
        .select("id")
        .eq("user_id", actor.id)
        .eq("property_id", propertyId)
        .maybeSingle();

      if (existing) {
        // Remove favorite
        await supabase
          .from("saved_properties")
          .delete()
          .eq("user_id", actor.id)
          .eq("property_id", propertyId);

        isCurrentlySaved = false;
        dbSuccess = true;
      } else {
        // Add favorite
        await supabase.from("saved_properties").insert({
          user_id: actor.id,
          property_id: propertyId,
        });

        isCurrentlySaved = true;
        dbSuccess = true;
      }
    } catch (dbErr) {
      console.warn("[Favorites DB Toggle Catch]:", dbErr);
    }

    // If DB wasn't reached or returned an error, toggle based on current state
    if (!dbSuccess) {
      isCurrentlySaved = !isCurrentlySaved;
    }

    // Sync to Redis cache
    if (isCurrentlySaved) {
      if (!cachedIds.includes(propertyId)) {
        cachedIds.push(propertyId);
      }
    } else {
      cachedIds = cachedIds.filter((id) => id !== propertyId);
    }
    await setCachedJson(cacheKey, cachedIds, CACHE_TTL.EXTENDED);

    return { success: true, isSaved: isCurrentlySaved };
  } catch (err: any) {
    console.warn("[Toggle Saved Property Catch]:", err);
    return { success: false, isSaved: false, error: err.message };
  }
}

/**
 * Fetch all saved/favorited properties for a user dynamically
 */
export async function listSavedProperties(
  actor: AuthenticatedUser
): Promise<any[]> {
  try {
    const cacheKey = `user:favorites:${actor.id}`;
    const cachedIds = (await getCachedJson<string[]>(cacheKey)) || [];
    const collectedIds = new Set<string>(cachedIds);
    const propertiesMap = new Map<string, any>();

    // 1. Fetch from Supabase
    try {
      const supabase = await createClient();
      const { data: savedRows, error } = await supabase
        .from("saved_properties")
        .select(`
          property_id,
          created_at,
          property:properties(
            id, slug, reference_code, title, description, listing_type, property_type,
            price, currency, bedrooms, bathrooms, total_area_sqm, plot_size_plots,
            title_type, is_off_plan, latitude, longitude, created_at, status,
            state:states(id, name, code),
            lga:lgas(id, name),
            agency:agencies(id, name, slug, logo_url, is_verified, phone, whatsapp_number),
            creator:users!properties_created_by_user_id_fkey(id, first_name, last_name, avatar_url, phone_number, whatsapp_number),
            images:property_images(url, is_primary)
          )
        `)
        .eq("user_id", actor.id)
        .order("created_at", { ascending: false });

      if (savedRows && !error) {
        for (const row of savedRows) {
          collectedIds.add(row.property_id);
          if (row.property) {
            propertiesMap.set(row.property_id, row.property);
          }
        }
      }
    } catch (dbErr) {
      console.warn("[List Saved Properties DB Catch]:", dbErr);
    }

    const allIds = Array.from(collectedIds);
    if (allIds.length > 0) {
      await setCachedJson(cacheKey, allIds, CACHE_TTL.EXTENDED);
    }

    // If the user has not saved any properties, return empty list!
    if (allIds.length === 0) {
      return [];
    }

    // Resolve any properties where join was not available (e.g. custom Redis properties or mock data)
    const result: any[] = [];
    for (const propId of allIds) {
      let prop = propertiesMap.get(propId);
      if (!prop) {
        prop = await getPropertyById(propId);
      }
      if (!prop) {
        prop = PROPERTIES_LIST.find((p) => p.id === propId);
      }
      if (prop) {
        result.push(prop);
      }
    }

    return result;
  } catch (err) {
    console.warn("[List Saved Properties Overall Catch]:", err);
    return [];
  }
}

/**
 * Check if a property is saved by a given user
 */
export async function checkIsSaved(
  userId: string,
  propertyId: string
): Promise<boolean> {
  try {
    const cacheKey = `user:favorites:${userId}`;
    const cachedIds = await getCachedJson<string[]>(cacheKey);
    if (cachedIds && cachedIds.includes(propertyId)) {
      return true;
    }

    const supabase = await createClient();
    const { data } = await supabase
      .from("saved_properties")
      .select("id")
      .eq("user_id", userId)
      .eq("property_id", propertyId)
      .maybeSingle();

    const isSaved = Boolean(data);
    if (isSaved && cachedIds && !cachedIds.includes(propertyId)) {
      cachedIds.push(propertyId);
      await setCachedJson(cacheKey, cachedIds, CACHE_TTL.EXTENDED);
    }
    return isSaved;
  } catch {
    return false;
  }
}

/**
 * Get list of all property IDs saved by a given user
 */
export async function getUserSavedPropertyIds(
  userId: string
): Promise<string[]> {
  try {
    const cacheKey = `user:favorites:${userId}`;
    const cachedIds = await getCachedJson<string[]>(cacheKey);
    if (cachedIds) return cachedIds;

    const supabase = await createClient();
    const { data } = await supabase
      .from("saved_properties")
      .select("property_id")
      .eq("user_id", userId);

    const ids = (data || []).map((d: any) => d.property_id);
    await setCachedJson(cacheKey, ids, CACHE_TTL.EXTENDED);
    return ids;
  } catch {
    return [];
  }
}
