export interface SubscriptionDoc {
  id: string;
  userId: string;
  productId?: string;
  plan: string;
  status: 'active' | 'trialing' | 'past_due' | 'cancelled' | 'none';
  currentPeriodStart?: string;
  currentPeriodEnd: string;
  cancelAtPeriodEnd: boolean;
  monthlyPrice?: number;
  tokensIncluded?: number;
  tokensUsed?: number;
  stripeCustomerId?: string;
  stripeSubscriptionId?: string;
  features?: string[];
  createdAt?: string;
  updatedAt?: string;
}

export interface PlanConfig {
  id: string;
  name: string;
  displayName: string;
  price: number;
  features: string[];
}

export interface SubscriptionClientOptions {
  baseUrl: string;
  productId: string;
  userId: string;
  getAccessToken: () => string;
}

export interface SubscriptionClient {
  getMySubscription(): Promise<SubscriptionDoc | null>;
  getPlans(): Promise<PlanConfig[]>;
  cancelSubscription(): Promise<SubscriptionDoc>;
  isPro(): boolean;
  isTrialing(): boolean;
  hasFeature(feature: string): boolean;
  daysRemaining(): number | null;
}

function trimTrailingSlash(url: string): string {
  return url.replace(/\/+$/, '');
}

export function createSubscriptionClient(opts: SubscriptionClientOptions): SubscriptionClient {
  const base = trimTrailingSlash(opts.baseUrl);
  let cached: SubscriptionDoc | null | undefined;

  async function request<T>(path: string, init?: RequestInit): Promise<T> {
    const token = opts.getAccessToken();
    const headers = new Headers(init?.headers);
    headers.set('Authorization', `Bearer ${token}`);
    headers.set('X-Product-Id', opts.productId);
    if (!headers.has('Content-Type') && init?.body !== undefined) {
      headers.set('Content-Type', 'application/json');
    }
    const res = await fetch(`${base}${path}`, { ...init, headers });
    if (res.status === 404) {
      return null as T;
    }
    if (!res.ok) {
      throw new Error(`Subscription API error: ${res.status} ${res.statusText}`);
    }
    if (res.status === 204) {
      return null as T;
    }
    const text = await res.text();
    if (!text) {
      return null as T;
    }
    return JSON.parse(text) as T;
  }

  function subscriptionFromCache(): SubscriptionDoc | null {
    if (cached === undefined || cached === null) {
      return null;
    }
    return cached;
  }

  return {
    async getMySubscription(): Promise<SubscriptionDoc | null> {
      const data = await request<SubscriptionDoc | null>('/billing/subscriptions/me');
      cached = data;
      return data;
    },

    async getPlans(): Promise<PlanConfig[]> {
      const data = await request<PlanConfig[] | { plans?: PlanConfig[] } | null>('/billing/plans');
      if (data == null) {
        return [];
      }
      if (Array.isArray(data)) {
        return data;
      }
      if (data && typeof data === 'object' && 'plans' in data && Array.isArray(data.plans)) {
        return data.plans;
      }
      return [];
    },

    async cancelSubscription(): Promise<SubscriptionDoc> {
      const data = await request<SubscriptionDoc>('/billing/subscriptions/cancel', {
        method: 'POST',
      });
      if (data === null) {
        throw new Error('Cancel subscription returned no body');
      }
      cached = data;
      return data;
    },

    isPro(): boolean {
      const sub = subscriptionFromCache();
      if (!sub) {
        return false;
      }
      const paid = sub.status === 'active' || sub.status === 'trialing';
      const planLower = sub.plan.toLowerCase();
      const notFree = planLower !== 'free' && planLower !== 'none';
      return paid && notFree;
    },

    isTrialing(): boolean {
      return subscriptionFromCache()?.status === 'trialing';
    },

    hasFeature(feature: string): boolean {
      const sub = subscriptionFromCache();
      if (!sub?.features?.length) {
        return false;
      }
      return sub.features.includes(feature);
    },

    daysRemaining(): number | null {
      const sub = subscriptionFromCache();
      if (!sub?.currentPeriodEnd) {
        return null;
      }
      const end = new Date(sub.currentPeriodEnd).getTime();
      if (Number.isNaN(end)) {
        return null;
      }
      const ms = end - Date.now();
      if (ms <= 0) {
        return 0;
      }
      return Math.ceil(ms / (1000 * 60 * 60 * 24));
    },
  };
}
