/**
 * types.ts — Services bundle type definitions.
 *
 * Spec 03 — request-scoped service bundle.
 *
 * Design notes:
 * - `db` is always available — DATABASE_URL is validated by defineApi.
 * - `push`, `email` have capability flags: `enabled` is set eagerly
 *   at bundle build time (cheap secret presence check); `client` is lazy.
 * - `storage` is always available (R2 binding is always present).
 * - `do` is always available (DO namespace bindings are always present).
 * - No module-level state. Bundle is per-request, lives on locals, GC'd after response.
 */

import type { SQL } from 'drizzle-orm';
import type Stripe from 'stripe';
import type { DrizzleDb } from '@/server/db/client.js';
import type { PushClient } from '@/server/push/types.js';
import type { ReactElement } from 'react';
import type { PaymentProvider } from '@/server/payments/provider.js';
import type { MockScenario } from '@/server/payments/mock/scenarios.js';
import type { CaseAlarmKind } from '@/server/do-client.js';
import type { MhPayload, VendorRegPayload } from '@/server/auth/cookies.js';
import type { ReturnsServiceDeps } from '@/server/returns/service.js';
import type { CarrierAdapter, CarrierKind } from '@/server/carriers/types.js';
import type {
  AnalyticsResult,
  BucketMode,
  BucketSize,
} from '@/server/admin/resources/analytics/queries.js';
import type { CaseWorkflowDeps } from '@/server/support/workflows/case.js';

// ─── Email client ─────────────────────────────────────────────────────────────

/**
 * Thin wrapper around Resend's sendEmail helper.
 * Handlers use this so they don't import Resend directly.
 */
export interface EmailClient {
  /** Send a transactional email. Resolves when the HTTP request completes. */
  sendEmail(opts: {
    to: string;
    subject: string;
    react: ReactElement;
    tags?: Array<{ name: string; value: string }>;
  }): Promise<{ success: boolean; messageId?: string; error?: string }>;
}

// ─── Storage client ───────────────────────────────────────────────────────────

/**
 * Thin wrapper around R2 + Cloudflare Images helpers.
 * Exposes the R2 bucket binding and image-related env for upload functions.
 */
export interface StorageClient {
  /** Raw R2 bucket binding for direct R2 operations. */
  bucket: R2Bucket;
  /** Public base URL for R2 objects (from env.PUBLIC_SITE_URL). */
  publicBase: string;
  /** QR signing secret (from env.QR_SECRET). */
  qrSecret: string;
  /**
   * Generate a QR PNG for a purchase token, upload it to R2, and return the
   * public URL. Wraps uploadPurchaseQr from storage/qr_png so workflows
   * don't import side-effect modules directly.
   */
  uploadQr(purchaseId: string, token: string): Promise<string>;
}

// ─── DO client ────────────────────────────────────────────────────────────────

/**
 * Thin wrapper around do-client arm/disarm helpers.
 * Binds env so handlers don't import env directly.
 *
 * Methods mirror the free functions in do-client.ts but without the env param.
 */
export interface DoClient {
  armDealAlarm(dealId: string, at: Date): Promise<void>;
  disarmDealAlarm(dealId: string): Promise<void>;
  armGoldWindowAlarm(dealId: string, at: Date): Promise<void>;
  disarmGoldWindowAlarm(dealId: string): Promise<void>;
  armGroupDealAlarm(
    groupDealId: string,
    at: Date,
    kind?: 'deadline' | 'partial_decision',
  ): Promise<void>;
  disarmGroupDealAlarm(groupDealId: string): Promise<void>;
  armPersonalOfferAlarm(requestId: string, at: Date): Promise<void>;
  disarmPersonalOfferAlarm(requestId: string): Promise<void>;
  armScheduledPublishAlarm(page: string, at: Date): Promise<void>;
  disarmScheduledPublishAlarm(page: string): Promise<void>;
  armSupportTicketAlarm(ticketId: string, at: Date): Promise<void>;
  disarmSupportTicketAlarm(ticketId: string): Promise<void>;
  armSupportCaseAlarm(caseId: string, kind: CaseAlarmKind, at: Date): Promise<void>;
  disarmSupportCaseAlarms(caseId: string): Promise<void>;
}

export interface CryptoService {
  key: string;
  blindIndex(value: string): Promise<string>;
  encrypt(value: string): SQL;
  decryptExpr(colExpr: SQL | string): SQL;
  safeDecryptExpr(colExpr: SQL | string): SQL;
  setUserPhone(value: string | null): Promise<{
    phone: SQL | string | null;
    phoneIndex: string | null;
    phoneHint: string | null;
  }>;
  setUserEmail(value: string | null): Promise<{
    email: SQL | string | null;
    emailIndex: string | null;
  }>;
}

export interface CookiesService {
  isHttps: boolean;
  buildCsrfCookieHeader(token: string): string;
  signVendorRegCookie(payload: VendorRegPayload): Promise<string>;
  verifyVendorRegCookie(raw: string): Promise<VendorRegPayload | null>;
  buildMh(
    user: {
      displayName: string | null;
      isAdmin: boolean;
      isVendor: boolean;
      isAffiliate: boolean;
      phoneHint?: string;
    },
    mhVersion: number,
    csrfToken: string,
  ): string;
  parseMh(raw: string | null | undefined): MhPayload | null;
  extractPhHint(raw: string | null | undefined): string | undefined;
  setMhCookie(headers: Headers, value: string): void;
  clearMhCookie(headers: Headers): void;
}

export interface AnalyticsService {
  vendor: {
    getFunnel(vendorId: string): Promise<unknown>;
  };
  admin: {
    getOverview(opts: { mode: BucketMode; bucket: BucketSize }): Promise<AnalyticsResult>;
  };
}

export interface ReturnsService {
  buildReturnsDeps(
    db: ReturnsServiceDeps['db'],
    payments: ReturnsServiceDeps['payments'],
  ): ReturnsServiceDeps;
}

export interface SupportCasesService {
  buildWorkflowDeps(db: CaseWorkflowDeps['db']): CaseWorkflowDeps;
}

export interface SharingService {
  shareBaseUrl: string;
}

export interface DevService {
  emailMockDb?: D1Database;
}

export interface ReferralsService {
  referralTestBypassSecret?: string;
  referralClicksDataset?: AnalyticsEngineDataset;
  cronSecret?: string;
  analyticsEnv: { CF_ACCOUNT_ID?: string; CF_AE_API_TOKEN?: string };
}

export interface VendorDealsService {
  buildWorkflowEnv(): { DATABASE_URL: string };
  enqueueOutbox(outboxId: string): Promise<void>;
}

export interface InvoicingService {
  credentialsKey?: string;
}

export interface QueuesService {
  cronSecret?: string;
  runLlmJobs(): Promise<void>;
  runOutbox(): Promise<void>;
}

export interface CarriersService {
  get(kind: CarrierKind): CarrierAdapter;
}

export interface PaymentService {
  (): Promise<PaymentProvider>;
  provider(): Promise<PaymentProvider>;
  stripe(): Promise<Stripe>;
  readMockScenario(): MockScenario | null;
}

// ─── Services bundle ─────────────────────────────────────────────────────────

/** Full services bundle attached to every request via locals.services. */
export type Services = {
  /** Drizzle DB client — always available. */
  db: DrizzleDb;

  /** Request-scoped crypto helpers bound to env.PII_KEY. */
  crypto: CryptoService;

  /** Request-scoped payment helpers. Callable for backward compatibility. */
  payments: PaymentService;

  /** Request-scoped analytics helpers bound to the request DB client. */
  analytics: AnalyticsService;

  /** Request-scoped carrier registry bound to the route-safe env subset. */
  carriers: CarriersService;

  /** Request-scoped returns workflow helpers. */
  returns: ReturnsService;

  /** Request-scoped support-case workflow helpers. */
  supportCases: SupportCasesService;

  /** Request-scoped sharing config. */
  sharing: SharingService;

  /** Request-scoped dev-only integrations. */
  dev: DevService;

  /** Request-scoped referral integrations. */
  referrals: ReferralsService;

  /** Request-scoped vendor-deal workflow helpers. */
  vendorDeals: VendorDealsService;

  invoicing: InvoicingService;

  queues: QueuesService;

  /** Request-scoped cookie helpers bound to the request security context. */
  cookies: CookiesService;

  /** Request-scoped alarm helpers. */
  alarms: DoClient;

  /** Web Push (VAPID). `enabled` is false when VAPID_* secrets are absent. */
  push: {
    enabled: boolean;
    client: Promise<PushClient>;
  };

  /** Email via Resend. `enabled` is false when RESEND_API_KEY is absent. */
  email: {
    enabled: boolean;
    client: EmailClient;
  };

  /**
   * Provider-neutral payment service. Concrete implementation
   * selected by `env.PAYMENT_PROVIDER`. Always available.
   *
   * Returns a Promise — call site must `await`. Provider modules are
   * loaded on first invocation (dynamic import) so the Stripe SDK is
   * never evaluated during SSR cold-start.
   */
  /** R2 + Cloudflare Images storage — always available. */
  storage: StorageClient;

  /**
   * Durable Object alarm helpers — always available.
   *
   * @deprecated Use `doClient` instead (Spec 07 naming).
   *   Both fields point at the same lazy factory; `do` removed in W6 cleanup.
   */
  do: DoClient;
  /** Durable Object alarm helpers (Spec 07 canonical name). */
  doClient: DoClient;
};

/**
 * Alias matching Spec 07 naming. Use `ServiceBundle` in new code; `Services`
 * retained for backward grep continuity until W6 cleanup.
 */
export type ServiceBundle = Services;

/** Keys of the Services bundle. Used for typed projection in defineApi. */
export type ServiceKey = keyof Services;
