import crypto from "crypto";
import { revalidatePath } from "next/cache";
import { createClient } from "@/lib/supabase/server";
import { createAdminClient } from "@/lib/supabase/admin";
import { AuthenticatedUser } from "@/types/auth";
import { BillingInterval, PaymentRow, SubscriptionPlanRow, SubscriptionRow } from "@/types/database";
import { logAuditEvent } from "./audit.service";
import { invalidateCache, cacheKeys, setCachedJson, getCachedJson, bumpCacheVersion, CACHE_TTL } from "@/lib/redis";
import { sendPaymentReceiptEmails } from "@/lib/services/email.service";
import { serverAnalytics as analytics } from "@/lib/analytics/server";


const PAYSTACK_SECRET_KEY = process.env.PAYSTACK_SECRET_KEY || "";
const PAYSTACK_BASE_URL = "https://api.paystack.co";

import { MARKETPLACE_PLANS } from "@/lib/constants/plans";
export { MARKETPLACE_PLANS };

/**
 * Initialize a Paystack checkout transaction for subscription upgrade
 */
export async function initializePaystackSubscription(
  actor: AuthenticatedUser,
  planId: "AGENT_PRO" | "AGENCY_GROWTH",
  billingInterval: BillingInterval = "MONTHLY",
  agencyId?: string | null
): Promise<{
  success: boolean;
  authorizationUrl?: string;
  reference?: string;
  error?: string;
}> {
  if (!PAYSTACK_SECRET_KEY) {
    return { success: false, error: "Paystack is not configured on the server" };
  }

  const plan = MARKETPLACE_PLANS[planId];
  if (!plan) {
    return { success: false, error: "Invalid subscription plan selected" };
  }

  const priceNgn = billingInterval === "ANNUAL" ? plan.priceAnnualNgn : plan.priceMonthlyNgn;
  const amountKobo = priceNgn * 100; // Paystack requires amount in Kobo

  const reference = `PAY-JAZ-${Date.now()}-${Math.floor(1000 + Math.random() * 9000)}`;
  const appUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
  const callbackUrl = `${appUrl}/dashboard/billing/callback`;

  const supabase = createAdminClient();

  // Ensure user exists in users table (for Clerk dev accounts that haven't triggered webhook)
  let targetUserId = actor.id;
  const { data: existingUser } = await supabase
    .from("users")
    .select("id")
    .eq("id", actor.id)
    .maybeSingle();

  if (!existingUser) {
    const { data: userByClerk } = await supabase
      .from("users")
      .select("id")
      .or(`clerk_id.eq.${actor.clerk_id},email.eq.${actor.email}`)
      .maybeSingle();

    if (userByClerk) {
      targetUserId = userByClerk.id;
    } else {
      const { data: insertedUser } = await supabase
        .from("users")
        .upsert(
          {
            id: actor.id,
            clerk_id: actor.clerk_id,
            email: actor.email,
            first_name: actor.first_name || "User",
            last_name: actor.last_name || "",
            is_active: true,
            updated_at: new Date().toISOString(),
          },
          { onConflict: "id" }
        )
        .select("id")
        .maybeSingle();

      if (insertedUser) {
        targetUserId = insertedUser.id;
      }
    }
  }

  // 1. Record pending payment in database via elevated admin client
  const paymentRecord = {
    reference,
    user_id: targetUserId,
    agency_id: agencyId || null,
    amount_kobo: amountKobo,
    currency: "NGN",
    status: "INITIALIZED" as const,
    purpose: "SUBSCRIPTION" as const,
    metadata: {
      planId,
      billingInterval,
      userEmail: actor.email,
      agencyId: agencyId || null,
      clerkId: actor.clerk_id,
    },
  };

  const { error: dbError } = await supabase.from("payments").insert(paymentRecord);

  if (dbError) {
    console.warn("[Payment Init DB Notice] Remote table payments not ready, persisting to Redis session cache:", dbError.message);
  }

  // Persist to Redis so payment verification and fulfillment succeed unconditionally
  await setCachedJson(cacheKeys.payment(reference), paymentRecord, CACHE_TTL.EXTENDED);

  // 2. Call Paystack API
  try {
    const res = await fetch(`${PAYSTACK_BASE_URL}/transaction/initialize`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${PAYSTACK_SECRET_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        email: actor.email,
        amount: amountKobo,
        reference,
        callback_url: callbackUrl,
        metadata: {
          planId,
          billingInterval,
          userId: actor.id,
          agencyId: agencyId || null,
          custom_fields: [
            {
              display_name: "Subscription Plan",
              variable_name: "plan_name",
              value: plan.name,
            },
          ],
        },
      }),
    });

    const data = await res.json();
    if (!data.status || !data.data?.authorization_url) {
      return { success: false, error: data.message || "Failed to initialize Paystack checkout" };
    }

    return {
      success: true,
      authorizationUrl: data.data.authorization_url,
      reference,
    };
  } catch (err: unknown) {
    const message = err instanceof Error ? err.message : "Network error contacting Paystack";
    return { success: false, error: message };
  }
}

/**
 * Verify Paystack Payment and Activate Subscription
 */
export async function verifyAndFulfillPaystackPayment(reference: string): Promise<{
  success: boolean;
  payment?: PaymentRow;
  error?: string;
}> {
  if (!PAYSTACK_SECRET_KEY) {
    return { success: false, error: "Paystack secret key is missing" };
  }

  const supabase = createAdminClient();

  // 1. Fetch existing payment record (from DB or Redis cache fallback)
  let payment: any = null;
  try {
    const { data: dbPayment } = await supabase
      .from("payments")
      .select("*")
      .eq("reference", reference)
      .maybeSingle();
    if (dbPayment) {
      payment = dbPayment;
    }
  } catch (err) {
    console.warn("[Payment Lookup DB Notice] Reading from Redis cache:", err);
  }

  if (!payment) {
    payment = await getCachedJson<any>(cacheKeys.payment(reference));
  }

  if (!payment) {
    return { success: false, error: "Payment reference not found" };
  }

  if (payment.status === "SUCCESSFUL") {
    // Already fulfilled idempotently
    return { success: true, payment: payment as PaymentRow };
  }

  // 2. Verify with Paystack API
  try {
    const res = await fetch(`${PAYSTACK_BASE_URL}/transaction/verify/${reference}`, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${PAYSTACK_SECRET_KEY}`,
      },
    });

    const result = await res.json();
    if (!result.status || result.data.status !== "success") {
      try {
        await supabase
          .from("payments")
          .update({
            status: "FAILED",
            paystack_gateway_response: result.data?.gateway_response || "Payment verification failed",
            updated_at: new Date().toISOString(),
          })
          .eq("reference", reference);
      } catch {}

      analytics.trackPaymentFailed(
        {
          reference,
          plan_id: payment.metadata?.planId,
          error_code: result.data?.gateway_response || "verification_failed",
          reason: "Payment verification failed",
        },
        payment.metadata?.clerkId || payment.user_id
      ).catch(() => {});

      return { success: false, error: "Payment was not successful or was cancelled" };
    }

    const paystackData = result.data;
    const metadata = payment.metadata as any;
    const planId = metadata?.planId;
    const billingInterval = metadata?.billingInterval || "MONTHLY";
    const agencyId = payment.agency_id || metadata?.agencyId;
    const clerkId = metadata?.clerkId;

    // 3. Mark payment as SUCCESSFUL (persisted to both DB and Redis)
    const updatedPayment: any = {
      ...payment,
      status: "SUCCESSFUL",
      paystack_channel: paystackData.channel,
      paystack_gateway_response: paystackData.gateway_response,
      paid_at: paystackData.paid_at || new Date().toISOString(),
      updated_at: new Date().toISOString(),
    };

    try {
      await supabase
        .from("payments")
        .update({
          status: "SUCCESSFUL",
          paystack_channel: paystackData.channel,
          paystack_gateway_response: paystackData.gateway_response,
          paid_at: paystackData.paid_at || new Date().toISOString(),
          updated_at: new Date().toISOString(),
        })
        .eq("reference", reference);
    } catch {}

    await setCachedJson(cacheKeys.payment(reference), updatedPayment, CACHE_TTL.EXTENDED);

    // 4. Update / Upsert Subscription in DB if available
    const daysToAdd = billingInterval === "ANNUAL" ? 365 : 30;
    const periodEnd = new Date(Date.now() + daysToAdd * 24 * 60 * 60 * 1000).toISOString();

    try {
      if (agencyId) {
        await supabase.from("subscriptions").upsert({
          agency_id: agencyId,
          user_id: null,
          plan_id: planId,
          status: "ACTIVE",
          current_period_start: new Date().toISOString(),
          current_period_end: periodEnd,
          updated_at: new Date().toISOString(),
        });
      } else {
        await supabase.from("subscriptions").upsert({
          user_id: payment.user_id,
          agency_id: null,
          plan_id: planId,
          status: "ACTIVE",
          current_period_start: new Date().toISOString(),
          current_period_end: periodEnd,
          updated_at: new Date().toISOString(),
        });
      }

      // Auto-assign roles upon successful subscription payment:
      if (payment.user_id) {
        if (planId === "AGENCY_GROWTH") {
          await supabase.from("user_roles").upsert(
            {
              user_id: payment.user_id,
              role_id: "AGENCY_ADMIN",
            },
            { onConflict: "user_id,role_id" }
          );

          // Activate the agency and owner membership record
          const targetAgencyId = agencyId || payment.agency_id;
          if (targetAgencyId) {
            await supabase
              .from("agencies")
              .update({ is_active: true, updated_at: new Date().toISOString() })
              .eq("id", targetAgencyId);

            await supabase.from("agency_members").upsert(
              {
                agency_id: targetAgencyId,
                user_id: payment.user_id,
                role: "ADMIN",
                is_active: true,
              },
              { onConflict: "agency_id,user_id" }
            );

            await invalidateCache(cacheKeys.agencyDetails(targetAgencyId));
          }
        } else if (planId === "AGENT_PRO") {
          await supabase.from("user_roles").upsert(
            {
              user_id: payment.user_id,
              role_id: "AGENT",
            },
            { onConflict: "user_id,role_id" }
          );
        }
      }
    } catch (dbUpsertErr) {
      console.warn("[Subscription Upsert Notice] DB table not ready, continuing:", dbUpsertErr);
    }

    // 5. Invalidate Entitlement Cache & User Session Cache for Paid Tiers
    await bumpCacheVersion("entitlements");
    if (clerkId) {
      await invalidateCache(cacheKeys.userSession(clerkId));
    }
    if (payment.user_id) {
      const { data: payerDbUser } = await supabase
        .from("users")
        .select("clerk_id")
        .eq("id", payment.user_id)
        .maybeSingle();

      if (payerDbUser?.clerk_id) {
        await invalidateCache(cacheKeys.userSession(payerDbUser.clerk_id));
      }
      await invalidateCache(cacheKeys.userSession(payment.user_id));
      await invalidateCache(cacheKeys.entitlements(payment.user_id));
    }
    const targetAgency = agencyId || payment.agency_id;
    if (targetAgency) {
      await invalidateCache(cacheKeys.agencyDetails(targetAgency));
      await invalidateCache(cacheKeys.entitlements(targetAgency));
    }

    try {
      revalidatePath("/dashboard");
      revalidatePath("/dashboard/agency");
      revalidatePath("/dashboard/billing");
      revalidatePath("/dashboard/properties");
    } catch {}

    let payerUser: any = null;
    if (clerkId) {
      payerUser = await getCachedJson<AuthenticatedUser>(cacheKeys.userSession(clerkId));
    }
    if (!payerUser && payment.user_id) {
      try {
        const { data: dbPayer } = await supabase.from("users").select("*").eq("id", payment.user_id).maybeSingle();
        payerUser = dbPayer;
      } catch {}
    }

    await logAuditEvent({
      actor: (payerUser ? { ...payerUser, roles: [], permissions: [] } : {
        id: payment.user_id,
        clerk_id: "",
        email: "",
        first_name: null,
        last_name: null,
        phone_number: null,
        whatsapp_number: null,
        avatar_url: null,
        is_active: true,
        is_suspended: false,
        suspended_reason: null,
        roles: [],
        permissions: [],
        created_at: "",
        updated_at: "",
      }) as any,
      action: "subscription.activated",
      entityType: "subscription",
      entityId: reference,
      newState: { planId, billingInterval, amountKobo: payment.amount_kobo },
    });

    // 7. Dispatch Payment Receipt & Settlement Alert
    sendPaymentReceiptEmails({
      payment: {
        amount: (payment.amount_kobo || 0) / 100,
        reference: payment.reference,
        purpose: payment.purpose || `Subscription Plan (${planId || "Plan"})`,
        payment_date: new Date().toLocaleDateString("en-NG", {
          year: "numeric",
          month: "short",
          day: "numeric",
        }),
        property_name: metadata?.propertyName || (planId ? `${planId.replace(/_/g, " ")} Plan` : "Nigeria Listing Subscription"),
      },
      payer: payerUser,
      landlordOrAgency: agencyId ? { name: metadata?.agencyName || "Agency" } : null,
    }).catch(() => {});

    analytics.trackPaymentSuccess(
      {
        reference: payment.reference,
        plan_id: planId,
        billing_interval: billingInterval,
        amount_ngn: (payment.amount_kobo || 0) / 100,
        currency: payment.currency || "NGN",
        channel: paystackData.channel,
        agency_id: agencyId,
      },
      clerkId || payment.user_id
    ).catch(() => {});

    return { success: true, payment: updatedPayment as PaymentRow };
  } catch (err: unknown) {
    const message = err instanceof Error ? err.message : "Failed to verify Paystack payment";
    return { success: false, error: message };
  }
}

/**
 * Verify Paystack Webhook Signature HMAC-SHA512
 */
export function verifyPaystackWebhookSignature(
  rawBody: string,
  signatureHeader: string | null
): boolean {
  if (!PAYSTACK_SECRET_KEY || !signatureHeader) return false;

  const hash = crypto
    .createHmac("sha512", PAYSTACK_SECRET_KEY)
    .update(rawBody)
    .digest("hex");

  return hash === signatureHeader;
}

/**
 * Retrieve current active subscription and payment history for a user/agency
 */
export async function getUserSubscriptionAndPayments(user: AuthenticatedUser): Promise<{
  subscription: SubscriptionRow | null;
  payments: PaymentRow[];
}> {
  const supabase = createAdminClient();
  const agencyId = user.agencyMembership?.agencyId;

  // 1. Query subscriptions table with elevated admin client
  let subscription: SubscriptionRow | null = null;
  try {
    let query = supabase
      .from("subscriptions")
      .select("*")
      .eq("status", "ACTIVE")
      .order("created_at", { ascending: false });

    if (agencyId) {
      query = query.or(`agency_id.eq.${agencyId},user_id.eq.${user.id}`);
    } else if (user.clerk_id) {
      query = query.or(`user_id.eq.${user.id},user_id.eq.${user.clerk_id}`);
    } else {
      query = query.eq("user_id", user.id);
    }

    const { data: subRows } = await query.limit(10);
    if (subRows && subRows.length > 0) {
      // Prioritize paid plans over FREE starter if multiple active records exist
      const paidSub = subRows.find((s) => s.plan_id === "AGENCY_GROWTH" || s.plan_id === "AGENT_PRO");
      subscription = (paidSub || subRows[0]) as SubscriptionRow;
    }
  } catch (err) {
    console.warn("[Subscription Fetch Warning]:", err);
  }

  // Fallback: If no DB subscription row returned yet, infer from active upgraded user roles
  if (!subscription || subscription.plan_id === "FREE") {
    if (user.roles.includes("AGENCY_ADMIN")) {
      subscription = {
        id: `sub-agency-${user.id}`,
        agency_id: agencyId || null,
        user_id: user.id,
        plan_id: "AGENCY_GROWTH",
        status: "ACTIVE",
        current_period_start: new Date().toISOString(),
        current_period_end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
        cancel_at_period_end: false,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      } as SubscriptionRow;
    } else if (user.roles.includes("AGENT")) {
      subscription = {
        id: `sub-agent-${user.id}`,
        agency_id: null,
        user_id: user.id,
        plan_id: "AGENT_PRO",
        status: "ACTIVE",
        current_period_start: new Date().toISOString(),
        current_period_end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
        cancel_at_period_end: false,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      } as SubscriptionRow;
    }
  }

  // 2. Query payments table with elevated admin client
  let payments: PaymentRow[] = [];
  try {
    let pQuery = supabase
      .from("payments")
      .select("*")
      .order("created_at", { ascending: false });

    if (agencyId) {
      pQuery = pQuery.or(`agency_id.eq.${agencyId},user_id.eq.${user.id}`);
    } else if (user.clerk_id) {
      pQuery = pQuery.or(`user_id.eq.${user.id},user_id.eq.${user.clerk_id}`);
    } else {
      pQuery = pQuery.eq("user_id", user.id);
    }

    const { data: pRows } = await pQuery.limit(50);
    if (pRows && pRows.length > 0) {
      payments = pRows as PaymentRow[];
    } else {
      // Secondary fallback query by metadata user email
      const { data: emailPRows } = await supabase
        .from("payments")
        .select("*")
        .contains("metadata", { userEmail: user.email })
        .order("created_at", { ascending: false })
        .limit(50);

      if (emailPRows && emailPRows.length > 0) {
        payments = emailPRows as PaymentRow[];
      }
    }
  } catch (err) {
    console.warn("[Payments Fetch Warning]:", err);
  }

  return { subscription, payments };
}
