import { Hono } from "hono";
import { createFactory } from "hono/factory";
import type { AppConfig } from "../config.js";
import {
  mapAppleProductToPlan,
  normalizeAppleSubscriptionStatus,
} from "../helpers/appleBilling.js";
import type { BaseEnv } from "../types/app.types.js";

type RevenueCatWebhookEvent = {
  aliases?: string[] | null;
  app_user_id?: string | null;
  environment?: string | null;
  expiration_at_ms?: number | null;
  id?: string | null;
  original_app_user_id?: string | null;
  original_transaction_id?: string | null;
  product_id?: string | null;
  purchased_at_ms?: number | null;
  store?: string | null;
  transaction_id?: string | null;
  type?: string | null;
};

type RevenueCatWebhookBody = {
  api_version?: string;
  event?: RevenueCatWebhookEvent;
};

const factory = createFactory<BaseEnv>();

const UUID_PATTERN =
  /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

const toIsoTimestamp = (value: number | null | undefined) =>
  typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : null;

const collectCandidateAppUserIds = (event: RevenueCatWebhookEvent) => {
  const uniqueIds = new Set<string>();

  for (const rawValue of [event.app_user_id, event.original_app_user_id, ...(event.aliases ?? [])]) {
    const value = rawValue?.trim();
    if (!value || !UUID_PATTERN.test(value)) {
      continue;
    }

    uniqueIds.add(value);
  }

  return Array.from(uniqueIds);
};

const resolveUserIdFromRevenueCatEvent = async (
  supabase: BaseEnv["Variables"]["supabase"],
  event: RevenueCatWebhookEvent
) => {
  const candidateIds = collectCandidateAppUserIds(event);
  if (candidateIds.length > 0) {
    const { data: profiles, error } = await supabase
      .from("profiles")
      .select("id")
      .in("id", candidateIds)
      .limit(1);

    if (error) {
      throw new Error(error.message);
    }

    const directMatch = (profiles ?? [])[0];
    if (directMatch?.id) {
      return directMatch.id as string;
    }
  }

  const lookupIds = [event.app_user_id, event.original_app_user_id, ...(event.aliases ?? [])].filter(
    (value): value is string => Boolean(value?.trim())
  );
  if (lookupIds.length === 0) {
    return null;
  }

  const { data: appleCustomers, error: appleCustomersError } = await supabase
    .from("apple_customers")
    .select("user_id, revenuecat_app_user_id")
    .in("revenuecat_app_user_id", lookupIds)
    .limit(1);

  if (appleCustomersError) {
    throw new Error(appleCustomersError.message);
  }

  return ((appleCustomers ?? [])[0]?.user_id as string | undefined) ?? null;
};

const handleRevenueCatWebhook = (config: AppConfig) =>
  factory.createHandlers(async (c) => {
    const authorization = c.req.header("authorization");
    if (authorization !== config.REVENUECAT_WEBHOOK_AUTH_HEADER) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const body = (await c.req.json()) as RevenueCatWebhookBody;
    const event = body.event;
    if (!event?.id || !event.type) {
      return c.json({ error: "Invalid RevenueCat payload" }, 400);
    }

    const supabase = c.get("supabase");

    const { data: existingEvent, error: existingEventError } = await supabase
      .from("apple_subscription_events")
      .select("id")
      .eq("event_id", event.id)
      .maybeSingle();

    if (existingEventError) {
      return c.json({ error: existingEventError.message }, 500);
    }

    if (existingEvent?.id) {
      return c.json({ received: true, duplicate: true });
    }

    const { error: insertEventError } = await supabase.from("apple_subscription_events").insert({
      environment: event.environment ?? null,
      event_id: event.id,
      event_type: event.type,
      payload: body,
    });

    if (insertEventError) {
      return c.json({ error: insertEventError.message }, 500);
    }

    const mappedPlan = mapAppleProductToPlan(event.product_id, {
      annualProductId: config.REVENUECAT_ANNUAL_PRODUCT_ID,
      soloProductId: config.REVENUECAT_SOLO_PRODUCT_ID,
    });

    if (!mappedPlan) {
      await supabase
        .from("apple_subscription_events")
        .update({ processed_at: new Date().toISOString() })
        .eq("event_id", event.id);

      return c.json({ ignored: true, received: true, reason: "unmapped_product" });
    }

    const ownerUserId = await resolveUserIdFromRevenueCatEvent(supabase, event);
    if (!ownerUserId) {
      await supabase
        .from("apple_subscription_events")
        .update({ processed_at: new Date().toISOString() })
        .eq("event_id", event.id);

      return c.json({ ignored: true, received: true, reason: "unknown_user" });
    }

    const normalizedStatus = normalizeAppleSubscriptionStatus(event.type, event.type === "CANCELLATION");

    const revenuecatAppUserId = event.app_user_id ?? event.original_app_user_id ?? ownerUserId;
    const currentPeriodEnd = toIsoTimestamp(event.expiration_at_ms);
    const currentPeriodStart = toIsoTimestamp(event.purchased_at_ms);

    const { error: appleCustomerError } = await supabase.from("apple_customers").upsert(
      {
        last_seen_environment: event.environment ?? null,
        original_app_user_id: event.original_app_user_id ?? null,
        revenuecat_app_user_id: revenuecatAppUserId,
        updated_at: new Date().toISOString(),
        user_id: ownerUserId,
      },
      { onConflict: "user_id" }
    );

    if (appleCustomerError) {
      return c.json({ error: appleCustomerError.message }, 500);
    }

    if (event.original_transaction_id) {
      const { error: appleSubscriptionError } = await supabase.from("apple_subscriptions").upsert(
        {
          cancel_at_period_end: normalizedStatus === "canceled",
          current_period_end: currentPeriodEnd,
          current_period_start: currentPeriodStart,
          environment: event.environment ?? "PRODUCTION",
          last_event_type: event.type,
          original_transaction_id: event.original_transaction_id,
          owner_user_id: ownerUserId,
          plan: mappedPlan,
          raw_customer_id: event.app_user_id ?? null,
          revenuecat_app_user_id: revenuecatAppUserId,
          revoked_at:
            normalizedStatus === "revoked" || normalizedStatus === "refunded"
              ? new Date().toISOString()
              : null,
          status: normalizedStatus,
          store: event.store ?? "APP_STORE",
          store_product_id: event.product_id ?? "",
          transaction_id: event.transaction_id ?? null,
          updated_at: new Date().toISOString(),
        },
        { onConflict: "original_transaction_id" }
      );

      if (appleSubscriptionError) {
        return c.json({ error: appleSubscriptionError.message }, 500);
      }
    }

    await supabase
      .from("apple_subscription_events")
      .update({ processed_at: new Date().toISOString() })
      .eq("event_id", event.id);

    return c.json({ received: true });
  });

export const getRevenueCatWebhookRouter = (config: AppConfig) => {
  const router = new Hono<BaseEnv>();
  router.post("/", ...handleRevenueCatWebhook(config));
  return router;
};
