/**
 * Browser/React Native-safe subscription client for platform-service.
 *
 * Wraps platform-service /subscriptions/* + /plans/* endpoints.
 * Caches subscription and plans for offline reads.
 * No Node.js dependencies — uses globalThis.fetch.
 */

import type {
  PlanConfig,
  SubscriptionClient,
  SubscriptionClientConfig,
  SubscriptionDoc,
} from './types.js';

function generateRequestId(): string {
  return typeof globalThis.crypto?.randomUUID === 'function'
    ? globalThis.crypto.randomUUID()
    : `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
}

export function createSubscriptionClient(config: SubscriptionClientConfig): SubscriptionClient {
  const { baseUrl, productId, userId, getAccessToken, storage } = config;

  const SUB_KEY = `${productId}-subscription`;
  const PLANS_KEY = `${productId}-plans`;

  let cachedSub: SubscriptionDoc | null = null;
  let cachedPlans: PlanConfig[] = [];

  // Restore from storage on creation
  if (storage) {
    try {
      const raw = storage.getItem(SUB_KEY);
      if (raw) cachedSub = JSON.parse(raw) as SubscriptionDoc;
    } catch {
      /* ignore */
    }
    try {
      const raw = storage.getItem(PLANS_KEY);
      if (raw) cachedPlans = JSON.parse(raw) as PlanConfig[];
    } catch {
      /* ignore */
    }
  }

  function headers(): Record<string, string> {
    const h: Record<string, string> = {
      'Content-Type': 'application/json',
      'x-product-id': productId,
      'x-request-id': generateRequestId(),
    };
    const token = getAccessToken();
    if (token) h['Authorization'] = `Bearer ${token}`;
    return h;
  }

  function persistSub(sub: SubscriptionDoc | null): void {
    cachedSub = sub;
    if (storage) {
      try {
        storage.setItem(SUB_KEY, JSON.stringify(sub));
      } catch {
        /* ignore */
      }
    }
  }

  function persistPlans(plans: PlanConfig[]): void {
    cachedPlans = plans;
    if (storage) {
      try {
        storage.setItem(PLANS_KEY, JSON.stringify(plans));
      } catch {
        /* ignore */
      }
    }
  }

  async function getMySubscription(): Promise<SubscriptionDoc | null> {
    const res = await globalThis.fetch(`${baseUrl}/subscriptions/${encodeURIComponent(userId)}`, {
      headers: headers(),
    });
    if (res.status === 404) {
      persistSub(null);
      return null;
    }
    if (!res.ok) throw new Error(`getMySubscription failed: ${res.status}`);
    const sub = (await res.json()) as SubscriptionDoc;
    persistSub(sub);
    return sub;
  }

  async function getPlans(): Promise<PlanConfig[]> {
    const res = await globalThis.fetch(`${baseUrl}/plans`, { headers: headers() });
    if (!res.ok) throw new Error(`getPlans failed: ${res.status}`);
    const data = (await res.json()) as { plans: PlanConfig[] };
    const plans = data.plans;
    persistPlans(plans);
    return plans;
  }

  async function startTrial(planName = 'pro'): Promise<SubscriptionDoc> {
    const res = await globalThis.fetch(`${baseUrl}/subscriptions`, {
      method: 'POST',
      headers: headers(),
      body: JSON.stringify({ userId, productId, plan: planName, status: 'trialing' }),
    });
    if (!res.ok) throw new Error(`startTrial failed: ${res.status}`);
    const sub = (await res.json()) as SubscriptionDoc;
    persistSub(sub);
    return sub;
  }

  async function cancelSubscription(): Promise<SubscriptionDoc> {
    const res = await globalThis.fetch(`${baseUrl}/subscriptions/${encodeURIComponent(userId)}`, {
      method: 'PUT',
      headers: headers(),
      body: JSON.stringify({ cancelAtPeriodEnd: true }),
    });
    if (!res.ok) throw new Error(`cancelSubscription failed: ${res.status}`);
    const sub = (await res.json()) as SubscriptionDoc;
    persistSub(sub);
    return sub;
  }

  async function updateSubscription(updates: Partial<SubscriptionDoc>): Promise<SubscriptionDoc> {
    const res = await globalThis.fetch(`${baseUrl}/subscriptions/${encodeURIComponent(userId)}`, {
      method: 'PUT',
      headers: headers(),
      body: JSON.stringify(updates),
    });
    if (!res.ok) throw new Error(`updateSubscription failed: ${res.status}`);
    const sub = (await res.json()) as SubscriptionDoc;
    persistSub(sub);
    return sub;
  }

  function isPro(): boolean {
    if (!cachedSub) return false;
    return (
      cachedSub.plan !== 'free' &&
      (cachedSub.status === 'active' || cachedSub.status === 'trialing')
    );
  }

  function isTrialing(): boolean {
    return cachedSub?.status === 'trialing' || false;
  }

  function hasFeature(feature: string): boolean {
    if (!cachedSub) return false;
    const plan = cachedPlans.find(p => p.name === cachedSub!.plan);
    if (!plan) return false;
    return plan.features.includes(feature);
  }

  function daysRemaining(): number | null {
    if (!cachedSub) return null;
    const end = new Date(cachedSub.currentPeriodEnd).getTime();
    const now = Date.now();
    const diff = end - now;
    if (diff <= 0) return 0;
    return Math.ceil(diff / (1000 * 60 * 60 * 24));
  }

  function getCachedSubscription(): SubscriptionDoc | null {
    return cachedSub;
  }

  function getCachedPlans(): PlanConfig[] {
    return cachedPlans;
  }

  async function refresh(): Promise<void> {
    await Promise.all([getMySubscription(), getPlans()]);
  }

  return {
    getMySubscription,
    getPlans,
    startTrial,
    cancelSubscription,
    updateSubscription,
    isPro,
    isTrialing,
    hasFeature,
    daysRemaining,
    getCachedSubscription,
    getCachedPlans,
    refresh,
  };
}
