import { createClient } from "@/lib/supabase/server";
import { createAdminClient } from "@/lib/supabase/admin";
import {
  LeadFilterInput,
  ScheduleViewingInput,
  UpdateViewingStatusInput,
} from "@/lib/validation/lead.schema";
import { AuthenticatedUser } from "@/types/auth";
import { LeadRow, LeadStatus, ViewingAppointmentRow, ViewingStatus } from "@/types/database";
import { logAuditEvent } from "./audit.service";

/**
 * List Leads for User or Agency CRM
 */
export async function listLeadsForUser(
  actor: AuthenticatedUser,
  filters: Partial<LeadFilterInput> = {}
) {
  const supabase = createAdminClient();
  const page = filters.page || 1;
  const limit = filters.limit || 20;
  const offset = (page - 1) * limit;

  let query = supabase
    .from("leads")
    .select(`
      *,
      property:properties(id, title, reference_code, price, currency, slug, images:property_images(url, is_primary)),
      viewing_appointments(id, scheduled_at, duration_minutes, meeting_type, meeting_location, status, notes, created_at)
    `, { count: "exact" });

  // Authorization scoping: Assigned to user, property assigned to or created by user, or agency leads, or customer client_email
  if (!actor.roles.includes("SUPER_ADMIN")) {
    const { data: userProps } = await supabase
      .from("properties")
      .select("id")
      .or(`created_by_user_id.eq.${actor.id},assigned_agent_id.eq.${actor.id}`);
    const propIds = (userProps || []).map((p: any) => p.id);

    const conditions: string[] = [`assigned_to_user_id.eq.${actor.id}`];
    if (propIds.length > 0) {
      conditions.push(`property_id.in.(${propIds.join(",")})`);
    }
    if (actor.agencyMembership?.agencyId) {
      conditions.push(`agency_id.eq.${actor.agencyMembership.agencyId}`);
    }
    if (actor.email) {
      conditions.push(`client_email.ilike.${actor.email}`);
      conditions.push(`email.ilike.${actor.email}`);
    }
    query = query.or(conditions.join(","));
  }

  if (filters.status && filters.status !== "ALL") {
    query = query.eq("status", filters.status);
  }

  if (filters.propertyId) {
    query = query.eq("property_id", filters.propertyId);
  }

  if (filters.search) {
    query = query.or(
      `client_name.ilike.%${filters.search}%,client_email.ilike.%${filters.search}%,client_phone.ilike.%${filters.search}%,full_name.ilike.%${filters.search}%,email.ilike.%${filters.search}%`
    );
  }

  query = query.order("created_at", { ascending: false }).range(offset, offset + limit - 1);

  const { data, count, error } = await query;

  return {
    leads: data || [],
    totalCount: count || 0,
    page,
    totalPages: Math.ceil((count || 0) / limit),
  };
}

/**
 * Aggregates CRM pipeline summary metrics
 */
export async function getLeadMetrics(actor: AuthenticatedUser) {
  const supabase = createAdminClient();

  let totalQuery = supabase.from("leads").select("id", { count: "exact", head: true });
  let newQuery = supabase.from("leads").select("id", { count: "exact", head: true }).eq("status", "NEW");
  let viewingQuery = supabase.from("leads").select("id", { count: "exact", head: true }).eq("status", "VIEWING_SCHEDULED");
  let wonQuery = supabase.from("leads").select("id", { count: "exact", head: true }).eq("status", "WON");

  if (!actor.roles.includes("SUPER_ADMIN")) {
    const { data: userProps } = await supabase
      .from("properties")
      .select("id")
      .or(`created_by_user_id.eq.${actor.id},assigned_agent_id.eq.${actor.id}`);
    const propIds = (userProps || []).map((p: any) => p.id);

    const conditions: string[] = [`assigned_to_user_id.eq.${actor.id}`];
    if (propIds.length > 0) {
      conditions.push(`property_id.in.(${propIds.join(",")})`);
    }
    if (actor.agencyMembership?.agencyId) {
      conditions.push(`agency_id.eq.${actor.agencyMembership.agencyId}`);
    }
    if (actor.email) {
      conditions.push(`client_email.ilike.${actor.email}`);
      conditions.push(`email.ilike.${actor.email}`);
    }
    const orFilter = conditions.join(",");
    totalQuery = totalQuery.or(orFilter);
    newQuery = newQuery.or(orFilter);
    viewingQuery = viewingQuery.or(orFilter);
    wonQuery = wonQuery.or(orFilter);
  }

  const [totalRes, newRes, viewingRes, wonRes] = await Promise.all([
    totalQuery,
    newQuery,
    viewingQuery,
    wonQuery,
  ]);

  const total = totalRes.count || 0;
  const won = wonRes.count || 0;
  const conversionRate = total > 0 ? Math.round((won / total) * 100) : 0;

  return {
    totalLeads: total,
    newLeads: newRes.count || 0,
    scheduledViewings: viewingRes.count || 0,
    closedDeals: won,
    conversionRate,
  };
}

/**
 * Get Specific Lead with Full Conversation Timeline
 */
export async function getLeadDetails(actor: AuthenticatedUser, leadId: string) {
  const supabase = createAdminClient();

  const { data: lead, error } = await supabase
    .from("leads")
    .select(`
      *,
      property:properties(id, title, reference_code, price, currency, slug, street_address, state_id, images:property_images(url, is_primary)),
      assigned_to:users!leads_assigned_to_user_id_fkey(id, first_name, last_name, email, avatar_url),
      activities:lead_activities(id, activity_type, note, metadata, created_at, created_by:users(first_name, last_name, avatar_url)),
      viewing_appointments(id, scheduled_at, duration_minutes, meeting_type, meeting_location, status, notes, created_at)
    `)
    .eq("id", leadId)
    .single();

  if (error || !lead) {
    return null;
  }

  // Authorization check
  const isSuperAdmin = actor.roles.includes("SUPER_ADMIN");
  const isAssigned = lead.assigned_to_user_id === actor.id;
  const isPropertyOwner = (lead.property as any)?.created_by_user_id === actor.id;

  if (!isSuperAdmin && !isAssigned && !isPropertyOwner) {
    return null;
  }

  return lead;
}

/**
 * Update Lead Status in CRM
 */
export async function updateLeadStatus(
  actor: AuthenticatedUser,
  leadId: string,
  newStatus: LeadStatus,
  lostReason?: string | null
): Promise<{ success: boolean; lead?: LeadRow; error?: string }> {
  const supabase = createAdminClient();

  const { data: updated, error } = await supabase
    .from("leads")
    .update({
      status: newStatus,
      lost_reason: lostReason || null,
      updated_at: new Date().toISOString(),
    })
    .eq("id", leadId)
    .select("*")
    .single();

  if (error || !updated) {
    return { success: false, error: error?.message || "Failed to update lead status" };
  }

  // Record activity
  await supabase.from("lead_activities").insert({
    lead_id: leadId,
    created_by_user_id: actor.id,
    activity_type: "STATUS_CHANGE",
    note: `Status updated to ${newStatus}${lostReason ? `: ${lostReason}` : ""}`,
  });

  await logAuditEvent({
    actor,
    action: "lead.status_updated",
    entityType: "lead",
    entityId: leadId,
    newState: { status: newStatus, lost_reason: lostReason },
  });

  return { success: true, lead: updated as LeadRow };
}

/**
 * Schedule a Physical or Virtual Viewing Appointment
 */
export async function scheduleViewingAppointment(
  actor: AuthenticatedUser,
  input: ScheduleViewingInput
): Promise<{ success: boolean; appointment?: ViewingAppointmentRow; error?: string }> {
  const supabase = createAdminClient();

  const { data: appointment, error } = await supabase
    .from("viewing_appointments")
    .insert({
      lead_id: input.leadId,
      property_id: input.propertyId,
      scheduled_by_user_id: actor.id,
      scheduled_at: new Date(input.scheduledAt).toISOString(),
      scheduled_for: new Date(input.scheduledAt).toISOString(),
      duration_minutes: input.durationMinutes,
      meeting_type: input.meetingType,
      meeting_location: input.meetingLocation || null,
      notes: input.notes || null,
      status: "SCHEDULED",
    })
    .select("*")
    .single();

  if (error || !appointment) {
    return { success: false, error: error?.message || "Failed to schedule viewing" };
  }

  // Update lead status to VIEWING_SCHEDULED
  await supabase
    .from("leads")
    .update({
      status: "VIEWING_SCHEDULED",
      updated_at: new Date().toISOString(),
    })
    .eq("id", input.leadId);

  await logAuditEvent({
    actor,
    action: "lead.viewing_scheduled",
    entityType: "lead",
    entityId: input.leadId,
    newState: appointment,
  });

  return { success: true, appointment: appointment as ViewingAppointmentRow };
}

/**
 * Update Viewing Appointment Status
 */
export async function updateViewingAppointmentStatus(
  actor: AuthenticatedUser,
  input: UpdateViewingStatusInput
): Promise<{ success: boolean; error?: string }> {
  const supabase = createAdminClient();

  const { error } = await supabase
    .from("viewing_appointments")
    .update({
      status: input.status,
      cancellation_reason: input.cancellationReason || null,
      updated_at: new Date().toISOString(),
    })
    .eq("id", input.appointmentId);

  if (error) {
    return { success: false, error: error.message };
  }

  await logAuditEvent({
    actor,
    action: "viewing.status_updated",
    entityType: "viewing_appointment",
    entityId: input.appointmentId,
    newState: { status: input.status },
  });

  return { success: true };
}

/**
 * List all Viewing Appointments for User
 */
export async function listViewingAppointmentsForUser(actor: AuthenticatedUser) {
  const supabase = createAdminClient();
  let query = supabase
    .from("viewing_appointments")
    .select(`
      *,
      lead:leads(id, client_name, client_email, client_phone),
      property:properties(id, title, reference_code, street_address, slug, images:property_images(url, is_primary))
    `)
    .order("scheduled_at", { ascending: false });

  if (!actor.roles.includes("SUPER_ADMIN")) {
    const { data: userProps } = await supabase
      .from("properties")
      .select("id")
      .or(`created_by_user_id.eq.${actor.id},assigned_agent_id.eq.${actor.id}`);
    const propIds = (userProps || []).map((p) => p.id);

    if (propIds.length > 0) {
      query = query.or(`scheduled_by_user_id.eq.${actor.id},property_id.in.(${propIds.join(",")})`);
    } else {
      query = query.eq("scheduled_by_user_id", actor.id);
    }
  }

  const { data } = await query;
  return data || [];
}
