import type { SupabaseClient } from "@supabase/supabase-js";

export type BillingPlan = "solo" | "team" | "annual";
export type RequiredBillingPlan = "solo" | "team";

type BillingProfileRow = {
  id: string;
  created_at: string | null;
  is_temporary: boolean | null;
  plan: string | null;
};

type StripeCustomerRow = {
  id: string;
  metadata: Record<string, string | null> | null;
};

type StripeSubscriptionRow = {
  id: string;
  status: string | null;
  created: number | null;
  current_period_end: number | null;
};

type StripeSubscriptionItemRow = {
  subscription: string;
  price: string | null;
  quantity: number | null;
};

type StripePriceRow = {
  id: string;
  lookup_key: string | null;
  metadata: Record<string, string | null> | null;
  product: string | null;
};

type StripeProductRow = {
  id: string;
  name: string | null;
  metadata: Record<string, string | null> | null;
};

export type OrganizationBillingState = {
  owner_user_id: string;
  member_count: number;
  trial_starts_at: string;
  trial_ends_at: string;
  is_trial_expired: boolean;
  required_plan: RequiredBillingPlan;
  required_team_quantity: number;
  active_subscription_plan: BillingPlan | null;
  active_subscription_quantity: number;
};

const ACTIVE_BILLING_STATUSES = ["active", "trialing", "past_due"];
const DEFAULT_BILLING_TRIAL_DAYS = 14;
const DEFAULT_BILLING_TRIAL_ROLLOUT_AT = "2026-03-08T00:00:00.000Z";

const parseTrialDays = () => {
  const parsed = Number.parseInt(process.env.BILLING_TRIAL_DAYS ?? "", 10);
  if (Number.isNaN(parsed) || parsed <= 0) {
    return DEFAULT_BILLING_TRIAL_DAYS;
  }

  return parsed;
};

export const parseTrialRolloutDate = (
  rawInput: string | null | undefined = process.env.BILLING_TRIAL_ROLLOUT_AT
) => {
  const raw = rawInput?.trim();
  const fallback = new Date(DEFAULT_BILLING_TRIAL_ROLLOUT_AT);

  if (!raw) {
    return fallback;
  }

  const parsed = new Date(raw);
  if (Number.isNaN(parsed.getTime())) {
    return fallback;
  }

  return parsed;
};

export const getOrganizationOwner = (profiles: BillingProfileRow[]) => profiles[0] ?? null;

export const getBillableMemberCount = (profiles: BillingProfileRow[]) =>
  profiles.filter((profile) => profile.is_temporary !== true).length;

export const getTrialWindow = (input: {
  ownerCreatedAt: Date;
  now: Date;
  trialDays: number;
  rolloutAt: Date | null;
}) => {
  const { ownerCreatedAt, now, trialDays, rolloutAt } = input;
  const trialStartDate = rolloutAt && rolloutAt > ownerCreatedAt ? rolloutAt : ownerCreatedAt;
  const trialEndDate = new Date(trialStartDate);
  trialEndDate.setUTCDate(trialEndDate.getUTCDate() + trialDays);

  return {
    trialStartDate,
    trialEndDate,
    isTrialExpired: now.getTime() > trialEndDate.getTime(),
  };
};

const resolveRequiredPlan = (memberCount: number): RequiredBillingPlan =>
  memberCount <= 1 ? "solo" : "team";

const statusWeight = (status: string | null | undefined) => {
  if (status === "active") return 3;
  if (status === "past_due") return 2;
  if (status === "trialing") return 1;
  return 0;
};

const planWeight = (plan: BillingPlan | null) => {
  if (plan === "annual") return 3;
  if (plan === "team") return 2;
  if (plan === "solo") return 1;
  return 0;
};

const inferBillingPlan = (planHint: string | null | undefined): BillingPlan | null => {
  const hint = (planHint ?? "").toLowerCase();

  if (
    hint.includes("founder") ||
    hint.includes("annual") ||
    hint.includes("beta") ||
    hint.includes("infinite")
  ) {
    return "annual";
  }

  if (hint.includes("team") || hint.includes("standard")) {
    return "team";
  }

  if (hint.includes("solo") || hint.includes("free")) {
    return "solo";
  }

  return null;
};

const normalizeProfilePlan = (plan: string | null | undefined): BillingPlan => {
  const normalized = (plan ?? "").toLowerCase();

  if (normalized === "annual" || normalized === "beta" || normalized === "founder") {
    return "annual";
  }

  if (normalized === "team" || normalized === "standard") {
    return "team";
  }

  return "solo";
};

const getPlanHint = (price: StripePriceRow | undefined, product: StripeProductRow | undefined) => {
  const parts = [
    price?.metadata?.plan,
    price?.metadata?.tier,
    price?.lookup_key,
    product?.metadata?.plan,
    product?.metadata?.tier,
    product?.name,
  ]
    .map((part) => part?.toLowerCase().trim())
    .filter((part): part is string => Boolean(part && part.length > 0));

  return parts[0] ?? null;
};

const getOrganizationProfiles = async (supabase: SupabaseClient, organizationId: number) => {
  const { data, error } = await supabase
    .from("profiles")
    .select("id, created_at, is_temporary, plan")
    .eq("organization_id", organizationId)
    .order("created_at", { ascending: true });

  if (error) {
    return { error: error.message, profiles: null };
  }

  const profiles = (data ?? []) as BillingProfileRow[];
  if (profiles.length === 0) {
    return { error: "Organization has no members", profiles: null };
  }

  return { error: null, profiles };
};

const resolveActiveSubscription = async (
  supabase: SupabaseClient,
  ownerUserId: string,
  ownerProfilePlan: string | null
): Promise<{ plan: BillingPlan | null; quantity: number }> => {
  const { data: customers, error: customersError } = await supabase
    .schema("stripe")
    .from("customers")
    .select("id, metadata")
    .limit(1000);

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

  const customer = (customers as StripeCustomerRow[] | null)?.find(
    (candidate) => candidate.metadata?.user_id === ownerUserId
  );

  if (!customer) {
    return { plan: null, quantity: 0 };
  }

  const { data: subscriptions, error: subscriptionsError } = await supabase
    .schema("stripe")
    .from("subscriptions")
    .select("id, status, created, current_period_end")
    .eq("customer", customer.id)
    .in("status", ACTIVE_BILLING_STATUSES);

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

  const normalizedSubscriptions = (subscriptions ?? []) as StripeSubscriptionRow[];
  if (normalizedSubscriptions.length === 0) {
    return { plan: null, quantity: 0 };
  }

  const ownerFallbackPlan = normalizeProfilePlan(ownerProfilePlan);

  const subscriptionIds = normalizedSubscriptions.map((subscription) => subscription.id);
  const { data: subscriptionItems, error: subscriptionItemsError } = await supabase
    .schema("stripe")
    .from("subscription_items")
    .select("subscription, price, quantity")
    .in("subscription", subscriptionIds);

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

  const normalizedItems = (subscriptionItems ?? []) as StripeSubscriptionItemRow[];
  const priceIds = Array.from(
    new Set(normalizedItems.map((item) => item.price).filter((price): price is string => !!price))
  );

  let pricesById = new Map<string, StripePriceRow>();
  let productsById = new Map<string, StripeProductRow>();

  if (priceIds.length > 0) {
    const { data: prices, error: pricesError } = await supabase
      .schema("stripe")
      .from("prices")
      .select("id, lookup_key, metadata, product")
      .in("id", priceIds);

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

    const normalizedPrices = (prices ?? []) as StripePriceRow[];
    pricesById = new Map(normalizedPrices.map((price) => [price.id, price]));

    const productIds = Array.from(
      new Set(
        normalizedPrices
          .map((price) => price.product)
          .filter((product): product is string => Boolean(product))
      )
    );

    if (productIds.length > 0) {
      const { data: products, error: productsError } = await supabase
        .schema("stripe")
        .from("products")
        .select("id, name, metadata")
        .in("id", productIds);

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

      const normalizedProducts = (products ?? []) as StripeProductRow[];
      productsById = new Map(normalizedProducts.map((product) => [product.id, product]));
    }
  }

  const candidates = normalizedSubscriptions.flatMap((subscription) => {
    const relatedItems = normalizedItems.filter((item) => item.subscription === subscription.id);

    if (relatedItems.length === 0) {
      return [
        {
          subscription,
          plan: null as BillingPlan | null,
          quantity: 1,
        },
      ];
    }

    return relatedItems.map((item) => {
      const price = item.price ? pricesById.get(item.price) : undefined;
      const product = price?.product ? productsById.get(price.product) : undefined;
      const hint = getPlanHint(price, product);

      return {
        subscription,
        plan: inferBillingPlan(hint),
        quantity: Math.max(1, item.quantity ?? 1),
      };
    });
  });

  if (candidates.length === 0) {
    return { plan: ownerFallbackPlan === "solo" ? "team" : ownerFallbackPlan, quantity: 1 };
  }

  candidates.sort((a, b) => {
    const aPlan = a.plan ?? "team";
    const bPlan = b.plan ?? "team";

    const byPlanWeight = planWeight(bPlan) - planWeight(aPlan);
    if (byPlanWeight !== 0) return byPlanWeight;

    const byStatusWeight =
      statusWeight(b.subscription.status) - statusWeight(a.subscription.status);
    if (byStatusWeight !== 0) return byStatusWeight;

    const aPeriodEnd = a.subscription.current_period_end ?? a.subscription.created ?? 0;
    const bPeriodEnd = b.subscription.current_period_end ?? b.subscription.created ?? 0;
    return bPeriodEnd - aPeriodEnd;
  });

  const winner = candidates[0];
  return {
    // Keep legacy fallback for unknown paid subscriptions but prefer owner profile plan
    // so founder/beta organizations do not downgrade to team because of metadata gaps.
    plan: winner.plan ?? (ownerFallbackPlan === "solo" ? "team" : ownerFallbackPlan),
    quantity: winner.quantity,
  };
};

export const getOrganizationBillingState = async (
  supabase: SupabaseClient,
  organizationId: number,
  nowInput: Date = new Date()
): Promise<{ data: OrganizationBillingState | null; error: string | null }> => {
  const { profiles, error: profilesError } = await getOrganizationProfiles(
    supabase,
    organizationId
  );
  if (profilesError || !profiles) {
    return { data: null, error: profilesError ?? "Failed to load organization members" };
  }

  const owner = getOrganizationOwner(profiles);
  if (!owner) {
    return { data: null, error: "Organization has no members" };
  }

  const ownerCreatedAt = owner.created_at ? new Date(owner.created_at) : nowInput;
  const trialWindow = getTrialWindow({
    ownerCreatedAt,
    now: new Date(nowInput),
    trialDays: parseTrialDays(),
    rolloutAt: parseTrialRolloutDate(),
  });

  const memberCount = getBillableMemberCount(profiles);
  const requiredPlan = resolveRequiredPlan(memberCount);

  try {
    const activeSubscription = await resolveActiveSubscription(supabase, owner.id, owner.plan);

    return {
      data: {
        owner_user_id: owner.id,
        member_count: memberCount,
        trial_starts_at: trialWindow.trialStartDate.toISOString(),
        trial_ends_at: trialWindow.trialEndDate.toISOString(),
        is_trial_expired: trialWindow.isTrialExpired,
        required_plan: requiredPlan,
        required_team_quantity: requiredPlan === "team" ? Math.max(1, memberCount) : 1,
        active_subscription_plan: activeSubscription.plan,
        active_subscription_quantity: activeSubscription.quantity,
      },
      error: null,
    };
  } catch (error) {
    return {
      data: null,
      error: error instanceof Error ? error.message : "Failed to resolve billing state",
    };
  }
};
