/**
 * @bytelyst/billing-client — Browser/React Native-safe billing client
 *
 * Wraps platform-service /plans, /subscriptions, /payments, /usage
 * endpoints with typed methods. Any ByteLyst product can add billing
 * with minimal wiring:
 *
 * @example
 * ```ts
 * import { createBillingClient } from '@bytelyst/billing-client';
 *
 * const billing = createBillingClient({
 *   baseUrl: 'http://localhost:4003/api',
 *   productId: 'notelett',
 *   getAccessToken: () => localStorage.getItem('notelett_access_token'),
 * });
 *
 * const plans = await billing.listPlans();
 * const sub = await billing.getSubscription();
 * await billing.changePlan('pro');
 * ```
 */

// ── Types ────────────────────────────────────────────────────

export type PlanTier = 'free' | 'pro' | 'enterprise';
export type SubscriptionStatus = 'active' | 'cancelled' | 'past_due' | 'trialing';
export type PaymentStatus = 'succeeded' | 'pending' | 'failed' | 'refunded';

export interface PlanConfig {
  id: string;
  productId: string;
  name: string;
  displayName: string;
  price: number;
  tokens: number;
  words: number;
  dictations: number;
  features: string[];
  stripePriceId?: string;
  active: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface Subscription {
  id: string;
  productId: string;
  userId: string;
  plan: PlanTier;
  status: SubscriptionStatus;
  currentPeriodStart: string;
  currentPeriodEnd: string;
  cancelAtPeriodEnd: boolean;
  monthlyPrice: number;
  tokensIncluded: number;
  tokensUsed: number;
  stripeCustomerId?: string;
  stripeSubscriptionId?: string;
  createdAt: string;
  updatedAt: string;
}

export interface Payment {
  id: string;
  productId: string;
  userId: string;
  amount: number;
  currency: string;
  status: PaymentStatus;
  description: string;
  method: string;
  invoiceUrl?: string;
  createdAt: string;
}

export interface UsageSummary {
  tokensUsed: number;
  tokensIncluded: number;
  tokensRemaining: number;
  percentUsed: number;
  currentPeriodStart: string;
  currentPeriodEnd: string;
}

// ── Config ───────────────────────────────────────────────────

export interface BillingClientConfig {
  /** Platform-service base URL (e.g. "http://localhost:4003/api"). */
  baseUrl: string;
  /** Product identifier. */
  productId: string;
  /** Returns current access token, or null if not authenticated. */
  getAccessToken: () => string | null;
  /** Request timeout in ms (default: 15000). */
  timeoutMs?: number;
}

// ── Client Interface ─────────────────────────────────────────

export interface BillingClient {
  /** List available plans for the product. */
  listPlans(): Promise<PlanConfig[]>;
  /** Get a specific plan by name. */
  getPlan(planName: string): Promise<PlanConfig>;
  /** Get current user's subscription. Returns null if no subscription. */
  getSubscription(): Promise<Subscription | null>;
  /** Change to a different plan (creates or updates subscription). */
  changePlan(plan: PlanTier): Promise<Subscription>;
  /** Cancel subscription at period end. */
  cancelSubscription(): Promise<Subscription>;
  /** Resume a cancelled subscription (undo cancel-at-period-end). */
  resumeSubscription(): Promise<Subscription>;
  /** List payment history. */
  listPayments(): Promise<Payment[]>;
  /** Get usage summary for current billing period. */
  getUsage(): Promise<UsageSummary>;
}

// ── Errors ───────────────────────────────────────────────────

export class BillingApiError extends Error {
  constructor(
    public readonly status: number,
    public readonly body: unknown,
    message?: string
  ) {
    super(message ?? `Billing API error ${status}`);
    this.name = 'BillingApiError';
  }
}

// ── Factory ──────────────────────────────────────────────────

export function createBillingClient(config: BillingClientConfig): BillingClient {
  const { baseUrl, productId, getAccessToken, timeoutMs = 15_000 } = config;

  async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      'x-product-id': productId,
    };

    const token = getAccessToken();
    if (token) headers['Authorization'] = `Bearer ${token}`;

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), timeoutMs);

    try {
      const res = await globalThis.fetch(`${baseUrl}${path}`, {
        method,
        headers,
        body: body != null ? JSON.stringify(body) : undefined,
        signal: controller.signal,
      });

      if (res.status === 204) return undefined as T;
      if (res.status === 404) return null as T;

      const json = await res.json().catch(() => ({}));

      if (!res.ok) {
        throw new BillingApiError(
          res.status,
          json,
          (json as Record<string, string>).message ?? `HTTP ${res.status}`
        );
      }

      return json as T;
    } finally {
      clearTimeout(timer);
    }
  }

  return {
    async listPlans() {
      const result = await request<{ plans: PlanConfig[] }>('GET', '/plans');
      return result.plans;
    },

    async getPlan(planName: string) {
      return request<PlanConfig>('GET', `/plans/${encodeURIComponent(planName)}`);
    },

    async getSubscription() {
      // Platform-service expects userId in path; the userId comes from the JWT.
      // We use a convenience endpoint that reads userId from the token.
      return request<Subscription | null>('GET', '/subscriptions/me');
    },

    async changePlan(plan: PlanTier) {
      return request<Subscription>('POST', '/subscriptions/me/change-plan', { plan });
    },

    async cancelSubscription() {
      return request<Subscription>('POST', '/subscriptions/me/cancel');
    },

    async resumeSubscription() {
      return request<Subscription>('POST', '/subscriptions/me/resume');
    },

    async listPayments() {
      const result = await request<{ payments: Payment[] }>('GET', '/payments/me');
      return result.payments;
    },

    async getUsage() {
      return request<UsageSummary>('GET', '/subscriptions/me/usage');
    },
  };
}
